var RTChartComponent = function(mainticker,reg,language) {
    //request data url 
    var hisUrl ="http://globalquote.morningstar.com/globalcomponent/RealtimeHistoricalStockData.ashx?";  // get historical data url
    var intradayDataUrl = "quote.jsp?"; // get intraday data url. 
    var sessionExpireRedirectUrl = "logout.jsp";
    var pushServer = "http://push"+((typeof(debug)!="undefined"&&debug)?"dev":"")+".morningstar.com/";  //push engine server.
    var getTickerInfoURL = "http://qt.morningstar.com/switchservice/getids.ashx?"; // get ticker information url.
    var qs_server="http://qt.morningstar.com/switchservice/qtswitch.ashx?symbol="; // ticker search url.
    var relatiedSecURL = "http://quote.morningstar.com/stock/competitorRawList.ashx?out=1";
    var chartOverTimeoutObj = null;
    //globalization parameters
    var region = (reg==null?"USA":reg);
    var lan = (language==null?"en_US":language);
    var cur="USD";
    
    // system configure  
    var latestTradeDate = new Date();
    var dateIndexBase = 24 * 60 * 60 * 1000;  // 
    var dateIndexStart = new Date(1900, 0, 1).getTime(); 
    var tickerMaxNum = 8;  // the maximum compaire ticker in a chart.
    var dailyStartDate;        // the start date for daily data.
    var weeklyStartDate;    // the start date for weekly data.
    var monthlyStartDate;  // the start date for monthly data. //2f4880
    var tickerColors = ["#728fb2", "#9b0033","#e96610","#f1ad02","#9faa00","#006065","#809aba","#e1b2c1","#e2e5b2","#293f6f","#fbe6b3","#f2a370","#b2cfd0", "#bec5d4","#c5cc66","#f8d1b7","#c36685","#2f4880","#d4dde8"];
    var objName; // the name of  self instance variable of RTChartComppnent.
    var chartObj = this; // self instance variable 
    
    // the size parameters for chart component.
    var outHeight;
    var pChartWidth=760;
    var vChartWidth=760;
    var pChartHeight=300;
    var vChartHeight=60;
    var miniChartWidth = 760;
    var miniChartHeight = 35;
    var minW = 550;  // the minimum of width of the chart component.
    
    // the id value of some elements.
    var outContainerId;
    var sliderID;
    var autoBoxID;
    var downlistID;
    var priceChartID;
    var volumeChartID;
    var miniChartID;
    
     // the jquery object refer to html elements
    var outObj;   // the jquery object refer to chart component outer element.
    var priceChartContainer=null, volChartContainer = null, miniChartContainer = null;
    var rElm,rgElm,lElm,lgElm,bLElm, bRElm,bMElm;  // slider elements
    var legendElm,crosshairLegendElm,crosshairLegendElmContainer;
    var lineXElm, lineYElm, xLabelElm, yLabelElm;
    var timeout = null;
    var dividendInfoElm;
    var addCompairBtElm,startBoxElm,endBoxElm,customRangeElm,indicatorWinElm;
    var chartStyleElm,chartStyleULElm,frequencyElm,freULElm,chartZoomElm,
		chartZoomULElm,eventULElm, chartCursorElm, chartCursorULElm, valuationULElm,indexsULElm,relatedSecULElm;
    var m1,m2,m3,mv;
    var lin;
    var rightMenuElm;
    var sliderCtrlElm;
    var chartOffset = {left:0,top:0};
    // control parameter.
    var mainTicker = mainticker;
    var compairTickers =[];
	var tickerInfos = {};
    var mainTickerInfo = {type:"1",tenforeTicker:""} ;
    var compairTickerInfo = [];
    var isDividend = false;
    var isSplits = false;
	var isFairValue  = false;
    
    var zoomRange = 1;
    var timeRangeType = "oneday"; 
    var chartStyle = "lines";
    var showVolume = true;
    var frequency = "minute";
    var chartCursor = 1; // 0 Track boll; 1 Crosshair
    var scaleType = 0; // 0 linear 1: log.
    
    var endDate = new Date();
    var startDate = endDate; // new Date(endDate.getFullYear(),endDate.getMonth(),endDate.getDate()-5);
    var dateFromFocus = false, dateToFocus = false; //custom date box
    var SubsciberTickers ={}; // the tickers be subscribed to push engine.
    
    //slider control parameters
    var pChartReady = false;
    var sliderObj = null;
    var slider_EndDate = latestTradeDate; //slider end date equal to last trade date.
    var slider_StartDate = new Date(slider_EndDate.getFullYear()-2,slider_EndDate.getMonth(),slider_EndDate.getDate()) ;//= getSliderStartDate(slider_EndDate);     //slider start date
    var rangSize = 512;  
    var miniChartFrequency = "d";
    var needDrawMiniChart = false; // need draw minichart when requestHistorical data.
    var sliderLeftMargin = 0;
    
    var needGetVolume = true;
    // UI system configure
    var UIConfig = {pCtrl1:{showCompair:true,showTimeRange:true,showFrequency:true,showZoom:true},
                          pCtrl2:{showChartType:true,showSMV:true, showEvent:true,showIndicator:true, showCursorType:true},
                          vCtrl:{showSMVV:true,showHideLink:true},
                          Chart:{showPChart:true,showVChart:true,showLegend:true,showTimeSlider:true},
                          RightMenu:true,
                          CustomUI:false,
                          chartCtrl:"default",
                          settingPanel:null,
						  fairValue:false,
                          autoHeight:false};
    
    // the flot object parameter.
    var p=null,v=null,m=null;
    var ticksize = 5;
    var Ticks = null;
    
    // data cache parameters.
    var DataList, VolList,DList,DividendInfoList;
    var SecurityData = [];
    var MiniChartData = [];
    // indicators parameters
    var indicatorCal = null;
    var indicatorUI = {  EMA:{title:"Exponential Moving Average (EMA)",parameter:["Line1 period","Line2 period","Line3 period"],defaultValue:[10,,]},
                 		 BBands:{title:"Bollinger Bands (BBands)",parameter:["Period","Deviation"],defaultValue:[20,2]},
                         PSAR:{title:"Parabolic SAR (PSAR)",parameter:["Step Period","Max Step Period"],defaultValue:[0.02,0.2]},
                         MACD:{title:"Moving Average Convergence/Divergence (MACD) ",parameter:["Slow Period","Fast Period","Signal Period"],defaultValue:[26,12,9]},
                         RSI:{title:"Relative Strength Index (RSI)",parameter:["Period"],defaultValue:[14]},
                         ROC:{title:"Rate of Change (ROC) ",parameter:["Period"],defaultValue:[12]},
                         WillR:{title:"Williams %R",parameter:["Period"],defaultValue:[14]},
                         SStochastic:{title:"Slow Stochastic",parameter:["K% Period","D% Period"],defaultValue:[15,5]},
                         FStochastic:{title:"Fast Stochastic",parameter:["K% Period","D% Period"],defaultValue:[5,3]}
                       };
    var indicatorFlots = {MACD:null,RSI:null,ROC:null,WillR:null,SStochastic:null,FStochastic:null};                         
    var indicatorVisiable = {EMA:false,BBands:false,PSAR:false,MACD:false,RSI:false,ROC:false,WillR:false,SStochastic:false,FStochastic:false};
    var indicatorsData = {SMA:{data:[null,null,null],num:[null,null,null],startPos:[null,null,null],colorIndex:[0,0,0],type:0},
						  SMAV:{data:null,num:null,startPos:null,colorIndex:0,type:0},
						  EMA:{data:[null,null,null],num:[null,null,null],colorIndex:[0,0,0],startPos:[null,null,null],type:0},
						  BBands:{data:null,parameter:[10,2],colorIndex:0,type:0},
	                      PSAR:{data:null,parameter:[0.02,0.2],colorIndex:0,type:0},
	                      MACD:{data:null,parameter:[26,12,9],type:1},
	                      RSI:{data:null,parameter:[14],type:1},
	                      ROC:{data:null,parameter:[12],type:1},
	                      WillR:{data:null,parameter:[14],type:1},
	                      SStochastic:{data:null,parameter:[15,5],type:1},
	                      FStochastic:{data:null,parameter:[5,3],type:1}
                         };
    var valuations = {  PE:{data:[],oFlot:null,show:false,code:"HS0A2",colorIndex:0,name:"Price/Earnings Ratio"},
						PS:{data:[],oFlot:null,show:false,code:"HS0A5",colorIndex:0,name:"Price/Sales Ratio"},
						PB:{data:[],oFlot:null,show:false,code:"HS0A0",colorIndex:0,name:"Price/Book Ratio"},
						PC:{data:[],oFlot:null,show:false,code:"HS0A1",colorIndex:0,name:"Price/Cash Ratio"},
						FairValue:{data:[],oFlot:null,show:false,code:"HS987",colorIndex:0,name:"Fair Value"}
					   }
    var mapValuationName = {HS0A2:"PE",HS0A5:"PS",HS0A0:"PB",HS0A1:"PC",HS987:"FairValue"};
	var indexsMap = {"0P00001G7J":false,"0P00001FJG":false,"0P00001G7B":false,"0P00001LXY":false};
    var $settingPanel = null;
    var dsettingPanel = null;
    // dropdown list component objects.
    var $ddlZoom, $ddlFrequency, $ddlChartType, $ddlCursorType,$ddlScaleType;
    var ddlFrequency, ddlZoom, ddlChartType, ddlCursorType,ddlScaleType;
    var zoomListData =  [{txt:"1 Day",val:"1"},{txt:"5 Days",val:"2"},{txt:"1 Month",val:"3"},{txt:"3 Months",val:"4"},
                                   {txt:"YTD",val:"5"},{txt:"1 Year",val:"6"},{txt:"3 Years",val:"7"},{txt:"5 Years",val:"8"},{txt:"10 Years",val:"9"},{txt:"Maximum",val:"10"},{txt:"Custom",val:"11"}];
    var frequencyListData = [{txt:"Minute",val:"minute"},{txt:"Daily",val:"d"},{txt:"Weekly",val:"w"},{txt:"Monthly",val:"m"}];
    var cursorTypeListData = [{txt:"Track Ball",val:"0"},{txt:"Crosshair",val:"1"},{txt:"Off",val:"3"}];
    var chartTypeListData = [{txt:"Line",val:"lines"},{txt:"OHLC",val:"ohlc"},{txt:"Candlestick",val:"candle"}];
	var scaleTypeListData = [{txt:"Linear",val:"0"},{txt: "Logarithmic",val:"1"}];
    this. $settingPanel =  $settingPanel;
    var hasConfirmBox = (typeof(Util) != "undefined" && typeof(Util.confirm) != "undefined") ? true:false;
    var hasPushProxy  = (typeof(push) != "undefined" && typeof(push.proxy) != null) ?true:false;
    //util get url parameters function
    var chartUtil = {
        args: null,
        queryString: function(key) {
            if (this.args == null) {
                this.args = {};
                var query = location.search.substring(1);
                var pairs = query.split("&");
                for (var i = 0; i < pairs.length; i++) {
                    var pos = pairs[i].indexOf('=');
                    if (pos == -1) {
                        continue;
                    }    
                    var argname = pairs[i].substring(0, pos);
                    var value = pairs[i].substring(pos + 1);
                    this.args[argname] = unescape(value);
                }
            }
            return this.args[key];
        }
    };
    
    this.pFlot = p;
    this.vFlot = v;
    this.mFlot = m;
	this.setLineColors = function(aColors){
		if (typeof(aColors) == "object" && aColors.length) {
			tickerColors = aColors;
		}
	}
	// after callback
	this.afterAddTickers = function(data){};
	this.afterRemoveTickers = function(data){};
	this.afterAddIndicators = function(data){};
	this.afterRemoveIndicator= function(data){};
	this.volumeChartChanged= function(data){};
	this.dividendChartChanged = function(data){};
	this.splitsChartChanged = function(data){};
	this.datePeriodChanged = function(data){};
	this.rangeChanged= function(data){};
	this.afterAcceptTickers = function(data){};
	this.afterChartTypeChanged = function(data){};
	this.chartMouseoverCallback = null;
	this.chartMouseOutCallback = null;
	
	var tabMenu, tabList=[{txt:"Line",id:1,closable:false},{txt:"OHLC",id:2,closable:false},{txt:"Candlestick",id:3,closable:false}]
    /*  intraday data structure*/
    
    function intrayData(slot) {
        this.HisData = [];// last four days.
        this.LatestDayData = [];
        this.HisVolumeData = [];
        this.LatestDayVolumeData = [];
        this.OpenTime = 570;
        this.CloseTime = 960;
        this.CachedData = [];
        this.VolumeCachedData = [];
        this.LatestYestClose=0;
        this.LatestDate= new Date();
        this.HisDate = [];
        this.HisYestClose = [];  
    }
    
    intrayData.prototype = {
        CopyIntradePriceData: function(ty,num) { 
            var sdata = [];
            var n=0;
            var spos = this.LatestYestClose;
            var st=0;
            if (num == 1) {
                n=0;
            } else if (num>1) { 
                n=num-1;
                if (n > this.HisData.length) {
                    n=this.HisData.length;  
                }
                spos= this.HisYestClose[n-1];
            }
            for (var k=n-1;k>=0;k--) {  
                 var tic = this.HisDate[k]==null?0:this.HisDate[k].getTime();
                 var d=this.HisData[k];
                 for (var g=0;g<d.length;g++) { 
                    var dpp = [];
                    if ((spos == null || isNaN(spos) || spos < 0) && !isNaN(d[g][1]) && d[g][1]>0) {
                        spos=d[g][1];
                    } //fix no yest close price bug;
                    dpp.push(d[g][0]+st);
                    if (ty=="return" && spos>0 && !isNaN(d[g][1])) {
                        dpp.push(100*(d[g][1]-spos)/spos);
                    } else {
                        dpp.push(d[g][1]);
                    }
                    dpp.push(tic+d[g][2]*60000);
                    sdata.push(dpp);
                 }     
                 st+=d.length;
            }
            if(n>1&&spos!=this.HisYestClose[n-1]) this.HisYestClose[n-1] = spos;//fix no yest close price bug;
            var tick = (this.LatestDate==null)?0:this.LatestDate.getTime();
            for (var h=0; h<this.LatestDayData.length; h++) {
                if (this.LatestDayData[h] == null || this.LatestDayData.length < 3) {
                    continue;
                }
                var dppp = [];
                if ((spos == null || isNaN(spos) || spos < 0) && this.LatestDayData[h].length>1 && !isNaN(this.LatestDayData[h][1]) && this.LatestDayData[h][1]>0) {
                 spos=this.LatestDayData[h][1];
                } //fix no yest close price bug;
                dppp.push(this.LatestDayData[h][0]+st);
                if (ty=="return" && spos > 0 && !isNaN(this.LatestDayData[h][1])) {
                 dppp.push(100*(this.LatestDayData[h][1]-spos)/spos);
                } else {
                 dppp.push(this.LatestDayData[h][1]);
                }
                dppp.push(tick+this.LatestDayData[h][2]*60000);
                sdata.push(dppp);
            }
            if (n == 0 && spos !=this.LatestYestClose) {
                this.LatestYestClose = spos;
            }//fix no yest close price bug;
            return sdata;
        },
      
        CalculateVolumeData:function(num) {
            var sdata = [],tdata= [];
            var n = 0, st=0; 
            if (num == 1) {
               n=0;//sdata =this.LatestDayVolumeData;
            } else if(num>1) { 
               n=num-1; 
               if (n > this.HisVolumeData.length) {
                   n=this.HisVolumeData.length;
               }
            }//spos = this.HisYestClose[n];
            for (var k=n-1;k>=0;k--) {
                var tic = this.HisDate[k]==null?0:this.HisDate[k].getTime();
                var d=this.HisVolumeData[k];
                var temp=0;
                for (var g=0;g<d.length;g++) {
                    sdata.push([d[g][0]+st,temp==0?NaN:(d[g][1]-temp)/1000000,tic+d[g][2]*60000]);
                    if (!isNaN(d[g][1])) {
                        temp=d[g][1];
                    }
                }
                st+=d.length;
            }
            var tick = (this.LatestDate==null)?0:this.LatestDate.getTime();
            var lsData = this.LatestDayVolumeData;
            temp = 0; 
            if (lsData != null && lsData.length > 0) {
                for (var h=0;h<lsData.length;h++) {
                    if(lsData[h].length > 2) {
                        sdata.push([lsData[h][0]+st,(temp==0?NaN:(lsData[h][1]-temp)/1000000),tick+lsData[h][2]*60000]);
                        if (!isNaN(lsData[h][1])) {
                            temp = lsData[h][1];
                        }
                    }         
                }
            }
            return sdata;   
        }       
    };
    
    /*        TS Data structure      */
    
    function timeSeriesData(slot) {
        this.StartIndex = 0;
        this.EndIndex = 0;
        this.Exchange="";
        this.Ticker="";
        this.Symbol ="";
        this.Timeslot = slot;
        this.Data = []; // the price time series Data.
        this.VolumeData = [];
		this.ValuationData = {PE:[],PB:[],PC:[],PS:[],FairValue:[]}; // ratio data.
    }
    
    timeSeriesData.prototype = {
        DividendFilter: function(stIndex,endIndex,dataType,isD,isF) {
            var dty = (isD)?1:-1;
            var sty =  isF?0:-1;
            var data, source = (dataType=="p")? this.Data:this.VolumeData;
            if (source!=null&&source.length>1) {
                data = [];
            } else {
                return null;
            }
            var st = this.StartIndex , end =  this.EndIndex;
            for (var j=0;j<source.length;j++) {
                if (source[j].DateIndex <stIndex || source[j].DateIndex > endIndex) {
                    continue;
                }
                if (source[j].Type == dty || source[j].Type == sty) {
                    data.push(source[j]);
                }
            }
            return data;
        },
        
        DataFilter :function(stIndex,endIndex,isFilled,dataType,dx) {        // filter the data for drawing chart.
            var slot  = this.Timeslot; 
            var data, source = null;
			if(dataType=="p"){
				source = this.Data;
			} else if(dataType=="v"){
				source = this.VolumeData;
			} else if(dataType == "f"){   //fair value data; 
				source = this.FairValueData;
			} else if(this.ValuationData[dataType] != undefined){ // ratio data.
				source = this.ValuationData[dataType];
			}
            if (source != null && source.length > 1) {
                data = [];
            } else {
                return null;
            }
            var st = source[0][source[0].length-1], end = source[source.length-1][source[source.length-1].length-1];
            var interVal = 1;
            if (slot == "daily"|| slot =="minute") {
                interVal = 1;
            } else if (slot == "weekly") {
                interVal = 7;
            } else if (slot == "monthly") {
                interVal = 30;
            }
            var inx = 0;
            if (isFilled && stIndex < st) {   
               for (var i = stIndex;i<st;i+=interVal) {
                    var dp ;
                    if (dataType == "p") { 
                        dp = [inx,NaN,NaN,NaN,NaN,NaN,i];
                    } else {
                        dp = [inx,NaN,i];
                    }
                    data.push(dp);
               inx++;
               }     
            }
            for (var j = 0; j < source.length; j += dx) {
                if (source[j][source[j].length-1] < stIndex || source[j][source[j].length-1] > endIndex) {
                    continue;
                }
                var dpp = [];
                dpp.push(inx);
                for(var h=1;h<source[j].length;h++) dpp.push(source[j][h]);
                data.push(dpp);
                inx++;
            }
            if (isFilled && endIndex > end) {   
               for (var g = end; g < endIndex; g += interVal) {
                    var d;
                    if (dataType=="p") {
                        d = [inx,NaN,NaN,NaN,NaN,NaN,g+interVal];
                    } else {
                    d = [inx,NaN,g+interVal];
                    }
                    data.push(d);
                    inx++;
               }     
            }
            return data;
        }
    };
    
    /*   data models   store the TS and intrayday data */
    
    function quoteData() {
        this.intradyDayData = new intrayData("minute");
        this.dailyData = new timeSeriesData("daily");
        this.weeklyData = new timeSeriesData("weekly");
        this.monthlyData = new timeSeriesData("monthly");
        this.dividendAndSplitData = new timeSeriesData("daily");
    }
    
    quoteData.prototype = {
        getDataByType: function(ty) {
            if (ty == "i") { 
                return this.intradyDayData;
            } else if (ty=="d") {
                return this.dailyData;
            } else if(ty=="w") {
                return this.weeklyData;
            } else if(ty=="m") {
                return this.monthlyData;
            } else if(ty=="s") {
                return this.dividendAndSplitData;
            } else {
                return null;
            }
        },
        saveTimeSeriesData: function(plist,vlist,flist,inxlist,freq,spos) {
            var isReturn = !isNaN(spos);
            var da = this.getDataByType(freq);
            if (da == null) {
                return;
            }
            da.Data = [];
            da.VolumeData = [];
			da.FairValueData = [];
            da.StartIndex = inxlist[0]; var s =0;
            if (inxlist.length > 4 && plist[0].length < 2 && inxlist[1]-inxlist[0] > inxlist[2]-inxlist[1]) {//remove the first point.
                s=1;da.StartIndex = inxlist[1]; 
            }
            da.EndIndex = inxlist[inxlist.length-1];
            for (var i = s; i < plist.length; i++) { 
                var dp = [];
                if (inxlist[i] <= 0) {
                    continue;
                }
                dp.push(i-s);
                if (isReturn) {           // return% convert to price
                    plist[i][0] = (plist[i][0]/100+1)*spos; 
                }
				if(plist[i]&&plist[i].length == 1){   // only close price.
					dp = dp.concat([plist[i][0],NaN,NaN,NaN]);
				}else if(!plist[i]||plist[i].length<4){  // invild data points
					dp = dp.concat([NaN,NaN,NaN,NaN]);
				}else{
					dp = dp.concat(plist[i]); // push the histrical price: close, high,low open.
				} 
                if (i > 0) {                                                     //1 :up, 0 down. this field is for drawing candlestick chart.
                    dp.push(plist[i][0] >=plist[i-1][0] ?1:0);
                } else {
                    dp.push(1);
                }
                dp.push(inxlist[i]);       // push dateIndex value;
                da.Data.push(dp);
                if ( vlist != null && i < vlist.length) {
                    da.VolumeData.push([i-s,vlist[i],inxlist[i]]);
                }
				if(flist != null && i < flist.length){
					da.FairValueData.push([i-s,flist[i],inxlist[i]]);
				}
            }
        },
		saveValuationData:function(plist,inxlist,freq,sName){
			var da = this.getDataByType(freq);
            if (da == null || !da.ValuationData[sName]) {
                return;
            }
			da.ValuationData[sName] = [];
			var s =0;
            for (var i = s; i < plist.length; i++) {
				var dp = [];
				if (inxlist[i] <= 0) {
					continue;
				}
				dp.push(i - s);
				dp.push(plist[i]); // push the histrical price: close, high,low open.			
				dp.push(inxlist[i]); // push dateIndex value;
				da.ValuationData[sName].push(dp);
			}
		}
    };
    
    /* calculate and cache technical indicators data */
    
    function indicatorCalculation() {
        this.cachedData= {smaData:{},emaData:{},bBandsData:{},macdData: {},rsiData:{},slowStochasticData:{},fastStochasticData:{},rocData:{},willRData:{},pSAR:{}};
    }
    
    indicatorCalculation.prototype = {
        filtrateCalculatedData: function(stIndex,endIndex,isFilled,aData,ty) {
            if (aData == null || aData.length<1) {
                return null;
            }
            var data  = [];
            var st = aData[0][aData[0].length-1], end = aData[aData.length-1][aData[aData.length-1].length-1];
            var interVal = 1;
            if (ty == 0 || ty == 1) { 
                interVal = 1;
            } else if (ty == 2) {
                interVal = 7;
            } else if (ty == 3) {
                interVal = 30;
            }
            var inx = 0;
            if (isFilled && stIndex < st) {   
                for (var i = stIndex; i < st; i += interVal) {
                     var dp = [inx,NaN,i];
                     data.push(dp);
                     inx++;
                }     
            }
            for (var j = 0; j < aData.length; j++) {
                if (aData[j][aData[j].length-1] < stIndex || aData[j][aData[j].length-1] > endIndex) {
                    continue;
                }
                var dpp = [];
                dpp.push(inx);
                for (var h=1;h<aData[j].length;h++) {
                    dpp.push(aData[j][h]);
                }
                data.push(dpp);
                inx++;
            }
            if (isFilled && endIndex > end) {   
                for (var g = end; g<endIndex; g+=interVal) {
                     var d = [inx,NaN,g+interVal];
                     data.push(d);
                     inx++;
                }     
            }
            return data;
        },
        calculateSma:function(aData,ty,n) {
            if (n == null || n< 0 || aData == null || aData.length < n) {
                return null;
            }
            var sCacheKey = ty+"_"+n,  aCachedData = this.cachedData.smaData;
            if (ty > 0 && ty != 10 && aCachedData[sCacheKey] != null) {                // ty>10 volume data ty=1 historical daily data; ty=2 historical weekily data; ty=3 historical monthly data; ty=0 intraydayData.
                return aCachedData[sCacheKey];
            }
            aCachedData[sCacheKey] = []; 
            var aCache = aCachedData[sCacheKey];
            var lg = aData.length, iSum = 0, m=0;
            for (var i = 0; i<lg; i++) {
                if (aData[i] != null && !isNaN(aData[i][1])) {
                   iSum += aData[i][1];
                   m++;
                }
                if (i < n-1) {
                    aCache.push([i,NaN,aData[i][aData[i].length-1]]);
                } else { 
                    var smv = isNaN(aData[i][1])||m<=0?NaN:(iSum/m); 
                    aCache.push([i,smv,aData[i][aData[i].length-1]]);
                    if (aData[i-n+1] != null && !isNaN(aData[i-n+1][1])) {
                        iSum-=aData[i-n+1][1];
                        m--;
                    }
                }           
            }
            return aCache;
        },
        
        calculateEma: function(aData,ty,n) {
            if (n == null || n< 0 || aData == null || aData.length < n) {
               return null;
            }
            var sCacheKey = ty+"_"+n; var aCachedData = this.cachedData.emaData;
            if (ty > 0 && aCachedData[sCacheKey] != null) {                                      // ty=1 historical daily data; ty=2 historical weekily data; ty=3 historical monthly data; ty=0 intraydayData.
               return aCachedData[sCacheKey]; 
            } 
            aCachedData[sCacheKey] = []; 
            var aCache = aCachedData[sCacheKey];
            var lg = aData.length, k  = 2/(n+1), iPreEma= 0, iSmv = 0,m=0;
            var needCut = true, st = 0;;
            for (var i=0;i<lg;i++) {
                if(needCut && (aData[i] == null || isNaN(aData[i][1]))) {   
                    aCache.push([i,NaN,aData[i][aData[i].length-1]]);
                    st = i+1;
                    continue;
                }
                needCut = false; 
                if ((i-st) < n-1) { 
                 aCache.push([i,NaN,aData[i][aData[i].length-1]]);
                 if (aData[i] != null && !isNaN(aData[i][1])) {
                     iSmv+=aData[i][1], m++;
                 }
                 continue;
                } else if ((i-st) == n-1) { 
                 if (aData[i] != null && !isNaN(aData[i][1])) {
                     iSmv+=aData[i][1], m++;
                 }
                 aCache.push([i,(iSmv == 0?NaN:(iSmv/m)),aData[i][aData[i].length-1]]);
                 if (!isNaN(aCache[i][1]) ) {
                     iPreEma = aCache[i][1];
                 }
                } else { 
                 var iCurPrice = (aData[i] ==null? NaN:aData[i][1]); 
                 var iEma = iPreEma + (iCurPrice-iPreEma)*k;
                 aCache.push([i,iEma,aData[i][aData[i].length-1]]); 
                 if (!isNaN(aCache[i][1])) { 
                     iPreEma = iEma;
                 }
                }
            }
            return aCache;
        },
        
        calculateBBands:function(aData,ty,n,dev) { 
            if (n< 0 || aData == null || aData.length < n) {
               return null;
            } 
            var sCacheKey = ty+"_"+n+"_"+dev; 
            var aCachedData = this.cachedData.bBandsData;
            if (ty>0 && aCachedData[sCacheKey] != null) {               // ty=1 historical daily data; ty=2 historical weekily data; ty=3 historical monthly data; ty=0 intraydayData.
                return aCachedData[sCacheKey];
            }  
            aCachedData[sCacheKey] = []; 
            var aCache = aCachedData[sCacheKey];
            var lg = aData.length; aCache[0] = [], aCache[1] = [];
            var iSum = 0, m = 0;
            for (var i = 0; i < lg; i++) { 
                if (i < n-1) {
                    aCache[0].push([i,NaN,aData[i][aData[i].length-1]]);aCache[1].push([i,NaN,aData[i][aData[i].length-1]]);
                    if (aData[i] != null && !isNaN(aData[i][1])) {
                        iSum += aData[i][1],
                        m++;
                    }
                    continue;
                } else { 
                    if (aData[i] != null && !isNaN(aData[i][1])) {
                        iSum+= aData[i][1];
                        m++;
                    } 
                    var iAvg = m>0?(iSum/m):NaN; var iStdDev = 0,iStdDevSum = 0,  iNum =0;
                    for (var j = i; j > (i-n); j--) {
                        if (aData[j] != null && !isNaN(aData[j][1])) {
                            iNum++;
                            iStdDevSum += (Math.pow((aData[j][1]-iAvg),2));
                        }
                    }
                    if(iNum > 1) iNum = iNum -1; // 
                    iStdDev = Math.sqrt(iStdDevSum/iNum);
                    var ibBandUpper = iAvg + dev*iStdDev;
                    var ibBandLower = iAvg - dev*iStdDev; 
                    aCache[0].push([i,ibBandUpper,aData[i][aData[i].length-1]]);
                    aCache[1].push([i,ibBandLower,aData[i][aData[i].length-1]]);
                    if (aData[i-n+1] != null && !isNaN(aData[i-n+1][1])) {
                       iSum -= aData[i-n+1][1] ;
                       m--;
                    }
                }                
            }
            return aCache;
        },
        
        calculateMACD:function(aData,ty,slow,fast,signal) {
            if (fast < 0 || aData == null || aData.length < fast) {
                return null;
            }
            var sCacheKey = ty+"_"+slow+"_"+fast+"_"+signal; var aCachedData = this.cachedData.macdData;
            if (ty>0 && aCachedData[sCacheKey] != null) {      // ty=1 historic
                return aCachedData[sCacheKey]; 
            }
            aCachedData[sCacheKey] = []; 
            var aCache = aCachedData[sCacheKey];
            aCache[0] = [], aCache[1] = [], aCache[2] = [];
            var slowEma = this.calculateEma(aData,ty,slow);
            var fastEma = this.calculateEma(aData,ty,fast);
            var lg = aData.length;
            var iMacd, divergence;
            for (var i = 0; i < lg; i++) {
                iMacd = fastEma[i][1] - slowEma[i][1];
                aCache[0][i] = [i,iMacd,aData[i][aData[i].length-1]];
            }
            var sigualEma = this.calculateEma(aCache[0],ty,signal);
            for (j = 0;j<lg;j++) { 
                divergence = aCache[0][j][1] - sigualEma[j][1];  
                aCache[2][j] = [j,divergence,aData[j][aData[j].length-1]];
            }
            aCache[1] = sigualEma;
            return aCache;
        },
        
        calculateRSI : function (aData,ty,n){
            if (n< 0 || aData == null || aData.length < n) {
               return null;
            }
            var sCacheKey = ty+"_"+n;
            var aCachedData = this.cachedData.rsiData;
            if (ty > 0 && aCachedData[sCacheKey] != null) {                             // ty=1 historical daily data; ty=2 historical weekily data; ty=3 historical monthly data; ty=0 intraydayData.
                return aCachedData[sCacheKey];
            } 
            aCachedData[sCacheKey] = []; 
            var aCache = aCachedData[sCacheKey], lg = aData.length;
            var iGain = 0, iLoss = 0,m = 0, iTemp = NaN, iTempLoss = NaN;
            for (var i = 0; i < lg; i++) {
                if (i < n) {
                    aCache[i] = [i,NaN,aData[i][aData[i].length-1]];
                    if (i>0 && !isNaN(aData[i][1])) {
                        var dx = aData[i][1] - aData[i-1][1]; 
                        if (dx > 0) { 
                            iGain+=dx;
                        } else {
                            iLoss+=dx;
                        }
                        m++;
                    }
                } else if (i == n) { 
                    var dx = aData[i][1] - aData[i-1][1]; 
                    if (dx > 0) { 
                       iGain += dx;
                    } else {
                       iLoss+=dx;
                    }
                    m++;
                    var iAvgGain = m>0?iGain/m:NaN, iAvgLoss = m>0?iLoss/m:NaN;
                    var iRsi = 100-(100/(1+Math.abs(iAvgGain/iAvgLoss)));
                    aCache[i] = [i,iRsi,aData[i][aData[i].length-1]];
                    iTemp = iAvgGain; 
                    iTempLoss = iAvgLoss;
                } else {
                    var dx = aData[i][1] - aData[i-1][1]; 
                    var dxLoss = dx<0 ?dx:0, dxGain = dx>0?dx:0;
                    var iAvgGain = (iTemp*(n-1)+dxGain)/n, iAvgLoss = (iTempLoss*(n-1)+dxLoss)/n;
                    var iRsi = 100-(100/(1+Math.abs(iAvgGain/iAvgLoss)));
                    aCache[i] = [i,iRsi,aData[i][aData[i].length-1]];
                    iTemp = iAvgGain; 
                    iTempLoss = iAvgLoss;
                }
              
            }
            return aCache;
        },
        
        _calculateStochastic: function(aData,ty,k,d,y) {
            if (k < 0 || aData == null || aData.length < k) { 
                return null;
            }
            var sCacheKey = ty+"_"+k+"_"+d;
            var aCachedData = (y=="slow"?this.cachedData.slowStochasticData:this.cachedData.fastStochasticData);
            if (ty > 0 && aCachedData[sCacheKey] != null) {       // ty=1 historic
                return aCachedData[sCacheKey];  
            }
            aCachedData[sCacheKey] = []; 
            var aCache = aCachedData[sCacheKey]; 
            aCache[0] = []; aCache[1] = [];
            if (ty == 0) {
                return [[],[]];  // intraday data.
            }
            var lg = aData.length;
            for (var i = 0; i< lg; i++) {
                if (i < (k - 1)) {
                    aCache[0][i] = [i,NaN,aData[i][aData[i].length-1]];
                } else { 
                    var iMax = 0; iMin = 1000000;
                    for (j = i; j > i-k; j--) {
                        if (aData[j][2] > iMax) {
                            iMax = aData[j][2];
                        }
                        if (aData[j][3] < iMin) {
                            iMin = aData[j][3];
                        }
                    }
                    var iK = (iMax<=iMin?NaN:100*(aData[i][1] - iMin)/(iMax-iMin));
                    aCache[0][i] = [i,iK,aData[i][aData[i].length-1]];
                }
              
            }
			var aSoure = aCache[0], lgg = aSoure.length, iSum = 0, m = 0;
                for (var h = 0;h < lgg; h++) {
		             if (h < (d-1)) {
		                 aCache[1][h] = [h,NaN,aSoure[h][2]]; 
		                 if (!isNaN(aSoure[h][1])) {
		                     iSum += aSoure[h][1];
		                     m++;
		                 } 
		             } else {
		                 if (!isNaN(aSoure[h][1])) {
		                     iSum+=aSoure[h][1];
		                     m++;
		                 } 
		                 aCache[1][h] = [h,(m>0?iSum/m:NaN),aSoure[h][2]];
		                 if (!isNaN(aSoure[h-d+1][1])) {
		                    iSum-=aSoure[h-d+1][1];
		                    m--;
		                 }
		             }
                }  
            return aCache;
        },
        
        calculateSlowStochastic:function(aData,ty,k,d) {
            return this._calculateStochastic(aData,ty,k,d,"slow");
        },
        
        calculateFastStochastic:function(aData,ty,k,d) {
            return this._calculateStochastic(aData,ty,k,d,"fast");
        },
        
       calculateROC:function (aData,ty,n) {
            if (n< 0 || aData == null || aData.length < n) {
                return null;
            }
            var sCacheKey = ty+"_"+n;
            var aCachedData = this.cachedData.rocData;
            if (ty > 0 && aCachedData[sCacheKey] != null) {      // ty=1 historic
               return aCachedData[sCacheKey];
            }  
            aCachedData[sCacheKey] = [];
            aCache = aCachedData[sCacheKey];
            var lg = aData.length;
			var preClose,roc;
            for (var i = 0;i < lg; i++) { 
                if (i < n) {
                    aCache.push([i,NaN,aData[i][aData[i].length-1]]);
                } else {
                    preClose = (aData[i-n] == null?NaN:aData[i-n][1]); 
                   	roc = NaN;
                    if (!isNaN(preClose)) {
                        roc = 100*(aData[i][1] - preClose)/preClose;
                    }
                    aCache[i] = [i,roc,aData[i][aData[i].length-1]];
                }
            }
            return aCache;
        },
        
        calculateWillR:function(aData,ty,n) { 
            if (n < 0 || aData == null || aData.length < n) {
                return null;
            }
            var sCacheKey = ty+"_"+n; 
            var aCachedData = this.cachedData.willRData;
            if (ty > 0 && aCachedData[sCacheKey] != null) {    // ty=1 historic
                return aCachedData[sCacheKey];
            } 
            aCachedData[sCacheKey] = []; 
            aCache = aCachedData[sCacheKey]; 
            var lg = aData.length;
            var iHigh = 0, iLow = 1000000,iR;
            for (var i = 0; i < lg; i++) { 
                if (i < n) {
                    if (aData[i] != null) {
						if (!isNaN(aData[i][2]) && aData[i][2] > iHigh) { 
                            iHigh = aData[i][2];
                        }
                        if (!isNaN(aData[i][3]) && aData[i][3] < iLow) {
                            iLow = aData[i][3];
                        }
                        if (i == n-1) { 
                            iR = 100*(aData[i][1]-iHigh)/(iHigh-iLow); 
                            aCache[i] = [i,iR,aData[i][aData[i].length-1]]; 
                        } else {
                            aCache[i] = [i,NaN,aData[i][aData[i].length-1]];
                        }
                    }
                } else {
                    iHigh = 0, iLow = 1000000;
                    for (var j = i; j >i - n; j--) {
                        if (!isNaN(aData[j][2]) && aData[j][2] > iHigh) {
                            iHigh = aData[j][2];
                        }
                        if (!isNaN(aData[j][3]) && aData[j][3] < iLow) {
                            iLow = aData[j][3];
                        }
                    }
                    iR = 100*(aData[i][1]-iHigh)/(iHigh-iLow);
					aCache[i] = [i,iR,aData[i][aData[i].length-1]];
                }         
            }
            return aCache;
        },
        
        calculatePSAR:function(aData,ty,a,maxA) {
            if (aData == null || aData.length < 2) {
                return;
            }
            var sCacheKey = ty+"_"+a+"_"+maxA; 
            var aCachedData = this.cachedData.pSAR;
            if (ty > 0 && aCachedData[sCacheKey] != null) {             // ty=1 historic
                return aCachedData[sCacheKey];
            }  
            aCachedData[sCacheKey] = [];
            aCache = aCachedData[sCacheKey];
            var lg = aData.length;
            var iTrend, iEp, iA = a, iSAR, iMax = 0, iMin = 1000000; 
            var preTrend = [,];
            aCache[0] = [0,NaN,aData[0][aData[0].length-1]];
            for (var i = 2; i <= lg; i++) { 
                if (i == 2) {
                    var mm = aData[0][2]>aData[1][2]?aData[0][2]:aData[1][2]; 
                    var nn = aData[0][3]<aData[1][3]?aData[0][3]:aData[1][3];
                    if (aData[2][1] >= aData[1][1]) {     // up trend
                        iTrend = 0; iSAR  = nn; 
                        iEp = mm; aCache[1] = [1,iSAR,aData[2][aData[2].length-1]]; 
                        preTrend[0] = preTrend[1] = 0;
                    } else {                                       // down trend.
                        iTrend  = 1; iSAR =mm; 
                        iEp = nn; aCache[1] = [1,iSAR,aData[1][aData[1].length]];
                        preTrend[0] = preTrend[1] = 0;
                    } 
                }
                var iCurrentSAR = iSAR + iA*(iEp-iSAR); 
                if (i == lg) {
                    aCache[i-1] = [i-1,iCurrentSAR,aData[i-1][aData[i-1].length-1]];
                    break;
                }
                if (isNaN(aData[i][1]) || isNaN(aData[i][2]) || isNaN(aData[i][3])) {
                    aCache[i-1] = [i,NaN,aData[i][aData[i].length-1]];
                    continue;
                }
                if (preTrend[0] == preTrend[1]) {
                    var min,max
                    if (preTrend[1] == 0) {                                                                  //up trend
                        if (i>1 && !isNaN(aData[i-1][3]) && !isNaN(aData[i-2][3])) {         
                            min = (aData[i-1][3]<aData[i-2][3]?aData[i-1][3]:aData[i-2][3]); 
                            if (iCurrentSAR > min) { 
                                iCurrentSAR = min;
                            }
                        }
                    } else {                                                                                      //down trend
                        if (i>1&& !isNaN(aData[i-1][2]) && !isNaN(aData[i-2][2])) {
                            max = (aData[i-1][2]>aData[i-2][2]?aData[i-1][2]:aData[i-2][2]);
                            if (iCurrentSAR < max) {
                                iCurrentSAR = max;
                            }
                        }
                    }             
                } else {
                    iCurrentSAR = iEp;
                }
                aCache[i-1] = [i-1,iCurrentSAR,aData[i-1][aData[i-1].length-1]];
                if (preTrend[1] == 1) {
                    if(aData[i][3] < iEp) { 
                        iEp = aData[i][3]; 
                        if (iA+0.02 <= maxA) iA = iA+0.02;
                     }
                } else {  
                    if (aData[i][2] > iEp) {
                        iEp = aData[i][2];
                        if (iA+0.02 <= maxA) iA = iA+0.02;
                    }
                }                  
                if ((preTrend[1]==0 && iCurrentSAR > aData[i][3]) || (preTrend[1] == 1 && iCurrentSAR < aData[i][2])) {
                    iA = a;
                    if (iTrend == 0) { 
                        iTrend = 1;
                    } else {
                        iTrend  = 0;
                    }
                } 
                iSAR = iCurrentSAR;
                preTrend [0] = preTrend[1];
                preTrend [1] = iTrend;
            }
            return aCache;
        }
        
    };
     
    this.Init = init;
    this.processData = processData;
    this.saveConfig = saveConfig;
    this.Draw = Draw;
    this.isConnectedToPushServer = false;
    this.SubsciberTickers = SubsciberTickers;
    this.setSliderLeftMargin = function (left){ sliderLeftMargin = left;};
    this.initUIConfig = initUIConfig;
    this.setLoginUrl = function(url){
        sessionExpireRedirectUrl = url;
    }
    this. setIntrayDataUrl = function(url) {
        intradayDataUrl = url;
    }
    this.clear = function() {
        SecurityData = null, DataList = null;
        if (sliderObj!=null) {
            sliderObj.onchange = null;
            sliderObj.onchanging = null;
            sliderObj.clear();
        }
        priceChartContainer.unbind("plothover");
        priceChartContainer.unbind("mouseout");
        if (p != null) {
            p.clear();
        }
        if (v != null) {
            v.clear();
        } 
        if (m != null) {
            m.clear();
        }
        if(indicatorCal != null) {
            indicatorCal = null;
        }
        for(var o in indicatorFlots) {
            if(indicatorFlots.hasOwnProperty(o) && indicatorFlots[o] != null) {
                indicatorFlots[o].clear();
            }
        }
        priceChartContainer.html("");
        volChartContainer.html("");
        miniChartContainer.html("");  
        priceChartContainer = null, volChartContainer = null, miniChartContainer = null;  
    };
    function getTickerInfo(pid){
		if(typeof(gIdMap)!= "undefined" && typeof(gData)!= "undefined"){  //QSII terminal
			return gData[gIdMap[pid]];
		} else {
			return tickerInfos[pid];
		}
	}
    function getUICtrlObjRef ($obj) { 
        addCompairBtElm = $obj.find("[idkey='addSecu']")
        customRangeElm = $obj.find("[idkey='customRange']");
        startBoxElm = $obj.find("[idkey='startBox']");
        endBoxElm = $obj.find("[idkey='endBox']");
        chartZoomElm = $obj.find("a[mname='zoom']");
        chartZoomULElm = $obj.find("UL[idkey='timeRange']");
        frequencyElm = $obj.find("a[mname='frequency']");
        freULElm = $obj.find("UL[idkey='freul']");
        chartStyleElm = $obj.find("a[mname='chartStyle']");
        chartStyleULElm = $obj.find("UL[idkey='divPlotStyle']");
        chartCursorElm = $obj.find("a[mname='cursorType']")
        chartCursorULElm = $obj.find("ul[idkey='divCursorType']");
        eventULElm = $obj.find("UL[idkey='eventUL']");
		valuationULElm = $obj.find("UL[idkey='valuationUL']");
		indexsULElm = $obj.find("UL[idkey='indexsUL']");
		relatedSecULElm = $obj.find("UL[idkey='relatedSecUL']");
        m1 = $obj.find("[idkey='mov1']"), m2=$obj.find("[idkey='mov2']"), m3=$obj.find("[idkey='mov3']"), mv=$obj.find("[idkey='movVol']");
        m1.val("Line 1"), m2.val("Line 2"), m3.val("Line 3"), mv.val("Line 1");   
        $ddlZoom = $obj.find(".ddlZoom"); 
        $ddlFrequency = $obj.find(".ddlFrequency");
        $ddlCursorType = $obj.find(".ddlCursorType");
        $ddlChartType = $obj.find(".ddlChartType");
		$ddlScaleType = $obj.find(".ddlScaleType");
    }
    function init(outID,objname,config) {  // init
        outContainerId = outID;
        objName = objname;
        //chartObj = obj;
        outObj = $("#"+outID);
        outObj.addClass("rtc-chart-out");
        sliderID = outID+"_sliderCtrl";
        autoBoxID = outID+"_autoBox";
        downlistID = outID+"_downlist";
        priceChartID = outID+"_priceChart";
        volumeChartID = outID+"_volChart";
        miniChartID = outID+"_miniChart";
        createUIElement();  // create UI elements.
        outObj.find("[idkey='autoBox']").attr("id",autoBoxID);
        outObj.find("[idkey='downlist']").attr("id",downlistID);
        outObj.find("[idkey='priceChart']").attr("id",priceChartID);
        outObj.find("[idkey='volChart']").attr("id",volumeChartID);
        outObj.find("[idkey='miniChart']").attr("id",miniChartID);
        outObj.find("[idkey='CM1']").attr("id",outID+"_CM1");
        priceChartContainer = $("#"+priceChartID);
        volChartContainer = $("#"+volumeChartID);
        miniChartContainer = $("#"+miniChartID);
        priceChartContainer.addClass(outID+"-rmenu"); // add right menu.
        rightMenuElm = $("#"+outID+"_CM1");
        indicatorWinElm = outObj.find("[idkey='win_indicator']");
        legendElm = outObj.find("[idkey='chartLegend']");
        crosshairLegendElmContainer = outObj.find(".rtc-legend-win");
        lineXElm = outObj.find(".rtc-line-x");
        lineYElm = outObj.find(".rtc-line-y");
        xLabelElm = lineXElm.find(":first-child");
        yLabelElm = lineYElm.find(":first-child");
        if (UIConfig.chartCtrl == "rtqPanel") {
            //lineYElm.css({top:"36px"});
            getUICtrlObjRef($settingPanel);
        } else {
            getUICtrlObjRef(outObj);
        }
        crosshairLegendElm = outObj.find("[idkey='win_legend']");
        dividendInfoElm = outObj.find("[idkey='DividendInfo']");
        lin = outObj.find("[idkey='volLink']");
        sliderCtrlElm = outObj.find("[idkey='sliderCtrl']");
        sliderCtrlElm.hide();// hide the silde control.
        initConfig(config); // init the user's configuration (chart state)
        initDropDownList();
        outObj.css({width:pChartWidth+"px"});
        priceChartContainer.css({width:pChartWidth+"px",height:pChartHeight+"px"});
        volChartContainer.css({width:vChartWidth+"px",height:vChartHeight+"px"});
        miniChartContainer.css({width:miniChartWidth+"px",height:miniChartHeight+"px"});
        connectToPusServer();
		var t = [];
		if(typeof(mainTicker)!= "undefined" && mainTicker!= null && mainTicker != "")t.push(mainTicker);
		if(compairTickers && compairTickers.length > 0){
			t = t.concat(compairTickers);
		}
		if(t.length > 0){
			validateSavedTickers(t);
		}	
    }
	function validateSavedTickers(symbol){
		if(typeof(chartObj.tValidation)=="object" && typeof(gIdMap)!= "undefined"&&typeof(gData)!="undefined"){//QSII terminal
			setTimeout(function(){chartObj.tValidation.checkTickers(symbol,function(o){
				var pid;
				compairTickers = [];
				if(o && o.length > 0){
					for(var i = 0;i < o.length;i++){
						pid = gData[o[i]]["performanceId"];
						SecurityData[pid] = new quoteData();
						if(!tickerInfos[pid]){
							tickerInfos[pid] = {exch:gData[o[i]]["exch"],ticker:gData[o[i]]["ticker"],gkey:o[i]};;
						}
						if(i==0){
							mainTicker = pid;
						}else {
							compairTickers.push(pid);
						}		
						requestIntrayData(pid,5);
					}
					initComponent();	
				}
			});},0);
		} else {
			var needSubscribe = [], needCheckMainticker = false;
			for(var i=0;i < symbol.length;i++){
				if(i==0 && !tickerInfos[symbol[i]]){
					needCheckMainticker = true;
				}else if(tickerInfos[symbol[i]]){
					requestIntrayData(symbol[i],5);
					needSubscribe.push(symbol[i]);
				}
			}
			if(needSubscribe.length > 0){   // subscribe compair tickers.
				bacthUnscribeTickers(needSubscribe);
			}
			if(needCheckMainticker){
				mainTicker = "";
				checkTickerInfo(symbol[0],objName+".SelectMainTicker"); // check main ticker.
			}else{
				initComponent();	
			}	
		}   
	}
	
    function validateMainTicker(symbol){
		if(typeof(chartObj.tValidation)=="object" && typeof(gIdMap)!= "undefined"&&typeof(gData)!="undefined"){//QSII terminal
			setTimeout(function(){chartObj.tValidation.checkTickers(symbol,function(o){
				if(o && o.length > 0){
					if(mainTicker == gData[o[0]]["performanceId"]){
						return;
					}
					if(mainTicker != ""){
						bacthUnscribeTickers([mainTicker]);
						delete tickerInfos[mainTicker];
					}
					mainTicker = gData[o[0]]["performanceId"];
					tickerInfos[mainTicker]={exch:gData[o[0]]["exch"],ticker:gData[o[0]]["ticker"],gkey:o[0]};
					initComponent();
					if (chartObj.setTitle) {
		                var t = tickerInfos[mainTicker]["ticker"];
		                chartObj.setTitle("Chart:"+t);
		            }
				}
			});},0);
		} else {
			setTimeout(function(){checkTickerInfo(symbol,objName+".SelectMainTicker");},0); //fix ie6 bug
		}   
	}
	function validateCompairTicker(symbol){
		if(typeof(chartObj.tValidation)=="object" && typeof(gIdMap)!= "undefined"&&typeof(gData)!="undefined"){//QSII terminal
			chartObj.tValidation.checkTickers(symbol,function(o){
				if(o && o.length > 0){
					var flag = false;
					var pid;
					for(var i = 0;i<o.length; i++){
						pid = gData[o[i]]["performanceId"];
						if(!pid || pid=="" || checkCompairTicker(pid)){
							continue;
						}
						if (compairTickers.length >= tickerMaxNum) {
		    		        if (hasConfirmBox) {
		                        Util.confirm("The maximum compair tickers are reached.",function(bFlg){return;});                                                                                              
		                    } else {
		                        break;
		                    }  
		                    break;
						}		
						compairTickers.push(pid) ;
						tickerInfos[pid]={exch:gData[o[i]]["exch"],ticker:gData[o[i]]["ticker"],gkey:o[i]};
						flag = true;
						SecurityData[pid] = new quoteData();		
						requestIntrayData(pid,5);
						if(indexsMap[pid]){
							indexsULElm.find("a[value='"+pid+"']").removeClass("rtq-chkbox_off");
						}
					}
					if(flag && zoomRange > 2){
						Draw(false);
					}
				}
			});
		} else {
			checkTickerInfo(symbol,objName+".SecruritySelected"); 
		}
	}
    this.acceptTicker = function(symbol)  { // change ticker interface.
        if (symbol != null && symbol !="") {
            validateMainTicker(symbol);
        }
    };
    function requestRelateSec(){
		var tt = tickerInfos[mainTicker]["ticker"];
		var gkey = tickerInfos[mainTicker]["gkey"];
		var rg = gData[gkey]["country"];
		var url = relatiedSecURL+'&t='+tt+'&region='+rg+'&cb='+objName+'.buildRelatedSec';
		$.getScript(url);
	}
	this.buildRelatedSec = function(data){
		if(data == null || !data.length) return;
		var str="";
		for(var i=0;i<data.length; i++){
			str+='<li><a catkey="indexs" name="'+data.Name+'" value="'+data.PID+'" menuitemstyle="trigger"';
			str+=' class="rtq-chkbox rtq-chkbox_off" href="javascript:void(0)">'+data.Name+'</a></li>';
		}
		
		relatedSecULElm.html(str);
	}
    function initComponent() { // init component state.
        chartObj.initUIEvents();
		if(UIConfig.chartCtrl == "rtqPanel"){
			//requestRelateSec();
		}
        bindUIstyle();
        DataList = VolList = DList = DividendInfoList = null;
        indicatorsData.MACD.data = indicatorsData.BBands.data = indicatorsData.ROC.data = null;
        indicatorsData.RSI.data = indicatorsData.SStochastic.data = indicatorsData.FStochastic.data = null;
        indicatorsData.WillR.data = indicatorsData.PSAR.data = null;
        indicatorsData.EMA.data = [null,null,null];
        if (indicatorCal != null) {
            indicatorCal.cachedData = {smaData:{},emaData:{},bBandsData:{},macdData: {},rsiData:{},slowStochasticData:{},fastStochasticData:{},rocData:{},willRData:{},pSAR:{}};
        }
        if (SecurityData == null) {
            SecurityData = [];
        }
        initStartDate();
        SecurityData[mainTicker] = new quoteData();
        requestIntrayData(mainTicker,5); // request intraday data.
        if (zoomRange > 2) {
            changeRange(zoomRange);
        } else {
            changeFrequrystyle(zoomRange);
        }
        setTimeout(function() {DrawMiniChart();},2000);
    }
     
    function initUIConfig(config) { // init the chart UI configuration.
        $.extend(UIConfig,config);
        dsettingPanel = UIConfig.settingPanel;
        if (dsettingPanel != null) {
            $settingPanel = $(dsettingPanel);
        }    
     }
 
    function createUIElement() {
        ddlChartType = ddlFrequency = ddlZoom = ddlCursorType = null;
        if (!UIConfig.CustomUI) outObj.html(""); // clear html.
        if (UIConfig.pCtrl2 != null && UIConfig.pCtrl2.showIndicator) outObj.append('<div class="rtc-win" idkey = "win_indicator" style="display:none"> <div class="rtc-win-wrap" > <div class="rtc-win-title"> <strong idkey = "title"></strong> <a class="rtc-win-close" idkey="close"></a> </div> <div class="rtc-win-content" idkey="content" ></div><div class="rtc-win-action"><a href="javascript:void(0)" class="qs-ui-btn ok" idkey ="drawBt">&nbsp;Draw&nbsp;</a> <a href="javascript:void(0)" idkey ="cancelBt" class="qs-ui-btn">Cancel</a> <a href="javascript:void(0)" idkey ="removeBt" class="qs-ui-btn">Remove</a> </div> </div> </div>');
        if (UIConfig.RightMenu) outObj.append('<ul idkey="CM1" class="rtrm-ot"><li class="rtrm-out" menuitemstyle="item"><a vkey="1" href="javascript:void(0)" class="rtrm-arrow">Chart Type</a><ul idkey="divPlotStyle"><li><a href="javascript:void(0)" menuitemstyle="trigger" value="lines" catkey="chartType">Line</a></li><li><a href="javascript:void(0)" menuitemstyle="trigger" value="ohlc" catkey="chartType">OHLC</a></li><li><a href="javascript:void(0)" menuitemstyle="trigger" value="candle" catkey="chartType">Candlestick</a></li></ul></li><li class="rtrm-out" menuitemstyle="item"><a href="javascript:void(0)" class="rtrm-arrow">Chart Cursor</a> <ul idkey="divCursorType"><li><a href="javascript:void(0)" menuitemstyle="trigger" value="0" catkey="cursorType">Track Ball</a></li><li><a href="javascript:void(0)" menuitemstyle="trigger" value="1" catkey="cursorType" class="on" >Crosshair</a></li><li><a href="javascript:void(0)" menuitemstyle="trigger" value="3" catkey="cursorType">Off</a></li></ul></li><li class="rtrm-out" menuitemstyle="item" ><a vkey="1" href="javascript:void(0)" class="rtrm-arrow">Frequency</a><ul idkey="freul"><li><a href="#" menuitemstyle="trigger" value="minute" catkey="frequency" class="on">Minute</a></li> <li><a href="#" menuitemstyle="trigger" value="d" catkey="frequency">Daily</a></li> <li><a href="#" menuitemstyle="trigger" value="w" catkey="frequency">Weekly</a></li><li><a href="#" menuitemstyle="trigger" value="m" catkey="frequency"> Monthly</a></li></ul></li><li class="rtrm-out" menuitemstyle="item"><a vkey="2" href="javascript:void(0)" class="rtrm-arrow">Zoom</a> <ul idkey="timeRange"><li><a menuitemstyle="trigger" catkey="zoom" value="1" href="#" class="on">1 Day</a></li><li><a menuitemstyle="trigger" catkey="zoom" value="2" href="#">5 Days</a></li><li><a menuitemstyle="trigger" catkey="zoom" value="3" href="#">1 Month</a></li><li><a menuitemstyle="trigger" catkey="zoom" value="4" href="#">3 Months</a></li><li><a menuitemstyle="trigger" catkey="zoom" value="5" href="#">YTD</a></li><li><a menuitemstyle="trigger" catkey="zoom" value="6" href="#">1 Year</a></li><li><a menuitemstyle="trigger" catkey="zoom" value="7" href="#">3 Years</a></li><li><a menuitemstyle="trigger" catkey="zoom" value="8" href="#">5 Years</a></li> <li><a menuitemstyle="trigger" catkey="zoom" value="9" href="#">10 Years</a></li><li><a menuitemstyle="trigger" catkey="zoom" value="10" href="#">Maximum</a></ul></li><li class="rtrm-out" menuitemstyle="item"><a idkey="rightmenu" vkey="1" href="#" class="rtrm-arrow">Event</a><ul idkey="eventUL"> <li><a menuitemstyle="trigger" value="dividend" name="dividends" catkey="event" class="rtq-chkbox rtq-chkbox_off">Dividend</a></li> <li><a class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="splits" name="splits" catkey="event" >Splits</a></li></ul></li></ul>');
        if (UIConfig.chartCtrl == "default" && UIConfig.pCtrl1 && UIConfig.pCtrl1 != null ) {
            var pc1 = document.createElement("div"); 
            var p1 = $(pc1); p1.addClass("rtc-ctrl1");
            if (UIConfig.pCtrl1.showCompair) p1.append('<span class="rtc-ctrl-lb1B" style="padding-left: 5px">Compare</span> <input idkey="autoBox" class="qs-ui-ipt" type="text" style="width: 90px;" value="" /> <a href="javascript:void(0)" idkey="addSecu" class="qs-ui-btn go">ADD</a> <div idkey="downlist" class="rtc-auto-drop"> </div>');
            if (UIConfig.pCtrl1.showTimeRange) p1.append('<div idkey="customRange" class="rtc-ctrl-sect"> <input type="text" idkey="startBox" style="width: 60px;" class="qs-ui-ipt" /><span class="rtc-ctrl-lb1">to</span><input type="text" idkey="endBox" style="width: 60px;" class="qs-ui-ipt" /> </div> ');
            if (UIConfig.pCtrl1.showFrequency) p1.append('<div class="rtc-ctrl-sect ddlFrequency"></div>');
            if (UIConfig.pCtrl1.showZoom) {
                p1.append('<div class="rtc-ctrl-sect ddlZoom"></div>');
            }
            outObj.append(pc1);
            pc1 = null; p1 = null;
        }
        if (UIConfig.chartCtrl == "default" && UIConfig.pCtrl2 && UIConfig.pCtrl2 != null) { 
            var pc2 = document.createElement("div");
            var p2 = $(pc2); p2.addClass("rtc-ctrl2"); p2.attr("idkey","chartControl");
            if (UIConfig.pCtrl2.showChartType) p2.append('<div class="rtc-ctrl-sect ddlChartType" style="padding-left: 0"> </div>');
            if (UIConfig.pCtrl2.showSMV) p2.append('<div class="rtc-ctrl-sect"><span class="rtc-ctrl-lb2B">Mov.Avg</span> <input type="text" idkey="mov1" style="width: 40px;" class="qs-ui-ipt" /> <input type="text" idkey="mov2" style="width: 40px;" class="qs-ui-ipt" /> <input type="text" idkey="mov3" style="width: 40px;" class="qs-ui-ipt" /> </div>');
            if (UIConfig.pCtrl2.showEvent) p2.append('<div class="rtc-ctrl-sect"><li menuitemstyle="item" idkey="eventList" class="rtc-pmdd1" style="height: 26px"><a class="rtc-pmddh1" href="javascript:void(0)" mname="event">Event</a><ul idkey="eventUL"> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="dividend" name="dividends" catkey="event"> Dividend</a></li> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="splits" name="splits" catkey="event">Splits</a></li> </ul> </li> </div>');
            if (UIConfig.pCtrl2.showIndicator) p2.append('<div class="rtc-ctrl-sect"> <li menuitemstyle="item" idkey="indicatorList" class="rtc-pmdd1" style="height: 26px; width:120px"><a class="rtc-pmddh1" href="javascript:void(0)" mname="indicator">Indicator</a><ul> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="EMA" name="EMA" catkey="indicator" >EMA</a></li> <li><a href="javascript:void(0)" menuitemstyle="trigger" class="rtq-chkbox rtq-chkbox_off" value="BBands" name="BBands" catkey="indicator">BBands</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="PSAR" name="PSAR" catkey="indicator">PSAR</a></li> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="MACD" name="MACD" catkey="indicator">MACD</a></li> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="RSI" name="RSI" catkey="indicator">RSI</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="ROC" name="ROC" catkey="indicator" >ROC</a></li> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="SStochastic" name="SStochastic" catkey="indicator">Slow Stochastic</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="FStochastic" name="FStochastic" catkey="indicator">Fast Stochastic</a></li> <li><a class="rtq-chkbox rtq-chkbox_off" href="javascript:void(0)" menuitemstyle="trigger" value="WillR" name="WillR" catkey="indicator">Williams %R</a></li> </ul> </ul> </li> </div>');
            outObj.append(pc2);
            pc2 = null; p2 = null;
        }
		if(UIConfig.chartCtrl == "rtqPanel"){ // add tab panel
			outObj.append('<ul class="rtc-ul-tab"><li class="rtc-tab"></li></ul>');
		}
        if (UIConfig.Chart && UIConfig.Chart != null && !UIConfig.CustomUI) { 
            if (UIConfig.Chart.showLegend) { 
                var le = document.createElement("div");
                $(le).addClass("rtc-legend-txt"); 
                $(le).attr("idkey","chartLegend"); 
                outObj.append(le);
            }
            var cha = document.createElement("div"); var cc = $(cha); cc.addClass("rtc-chart-container");
            if (UIConfig.Chart.showPChart) {
				cc.append('<div class="rtc-legend-win" style="display:none;"><div class="rtc-legend-win-wrap" idkey = "win_legend"></div></div>');
				cc.append('<div class="rtc-line-x"><span></span></div><div class="rtc-line-y"><span></span></div><div idkey="priceChart" class="rtc-chart rtc-chart-main"></div>');
			}
			if (UIConfig.chartCtrl == "default" && UIConfig.vCtrl && UIConfig.vCtrl != null) {
                var vc = document.createElement("div"); vcc =$(vc); vcc.addClass("rtc-ctrl2");  
                vcc.append('<div class="rtc-ctrl-sect"><span class="rtc-ctrl-lb2B">Volume (mil)</span></div>');
                if (UIConfig.vCtrl.showSMVV) vcc.append(' <div class="rtc-ctrl-sect"><span class="rtc-ctrl-lb2B">Mov.Avg</span><input idkey="movVol" style="width: 40px;" class="qs-ui-ipt" type="text" /></div>'); 
                if (UIConfig.vCtrl.showHideLink) vcc.append('<div class="rtc-ctrl-sect" style="float: right;"><a href="#" idkey="volLink" class="rtc-ctrl-lb2B">Hide</a></div>');
                cc.append(vc); vc = null;
            } else if (UIConfig.chartCtrl == "rtqPanel") {
                cc.append('<div class="rtc-vol-handle" idkey = "volLink"></div>')
            }
            if (UIConfig.Chart.showVChart) cc.append('<div idkey="volChart" class="rtc-chart rtc-chart-vol"></div>');
			cc.append('<div idkey="DividendInfo" class="rtc-message-label"></div>');
            outObj.append(cc);
			if (UIConfig.Chart.showTimeSlider) outObj.append('<div class="rtc-chart-container"><div idkey="miniChart" class="rtc-chart"></div></div>');     
            cc = null;
        }
        if(UIConfig.chartCtrl == "rtqPanel") createRtqPanelCtrlUI();
    }
    
    function createRtqPanelCtrlUI () {
        var str = "";
        str += '<table cellpadding="0" cellspacing="0" class="rtq-setting-tbl">';
        if (UIConfig.pCtrl1.showCompair) {
			str += '<tr> <td colspan="2"><Strong>Compare</Strong> </td> </tr> <tr> <td colspan="2"> <input id="' + autoBoxID + '" idkey="autoBox" class="qs-ui-ipt" type="text" style="width: 110px;" value="" /> <a href="#" idkey="addSecu" class="qs-ui-btn go">ADD</a> <div idkey="downlist" id="' + downlistID + '" class="rtc-auto-drop"> </div> </td> </tr>';
			
			str +='<tr><td colspan ="2" class="rtc-setting-row"><ul><li menuitemstyle="item" idkey="indexsList" class="rtc-pmtxt" style="width:150px" ><a href="javascript:void(0)" mname="indexs">Indexes</a><ul idkey="indexsUL"><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="0P00001G7J" name="IXUS:SPX" catkey="indexs" >S&P 500</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="0P00001FJG" name="IXUS:$INDU" catkey="indexs" >DJIA</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="0P00001G7B" name="IXUS:COMP" catkey="indexs" >NASDAQ</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="0P00001LXY" name="IXUS:RUT" catkey="indexs" >Russell 2000</a></li></ul></li></ul></td></tr>';
			//str +='<tr><td colspan ="2" class="rtc-setting-row"><ul><li menuitemstyle="item" idkey="relatedSecList" class="rtc-pmtxt" style="width:150px" ><a href="javascript:void(0)" mname="relatedSec">Related Securities</a><ul idkey="relatedSecUL"></ul></li></ul></td></tr>';
		}
		str+='<tr><td colspan ="2"><Strong>Scale</Strong></td></tr> <tr><td colspan="2"> <div class="rtc-setting-row ddlScaleType"> </div></td> </tr>';
        //jacky trend line
		str += '<tr><td colspan="2"><input type="button" value="Add Trendline" name="addTrendLine" class="rtq-chart-add-trendline"/></td></tr>';
		if (UIConfig.pCtrl1.showTimeRange) str += '<tr> <td colspan="2"> <Strong>Range</Strong> </td> </tr> <tr> <td colspan="2"> <div idkey="customRange"> <input type="text" idkey="startBox" style="width: 62px;" class="qs-ui-ipt" /><span class="rtc-ctrl-lb1">to</span><input type="text" idkey="endBox" style="width: 62px;" class="qs-ui-ipt" /> </div> </td> </tr>';
        if (UIConfig.pCtrl1.showFrequency && UIConfig.pCtrl1.showFrequency) {
            str+=' <tr><td ><Strong>Zoom</Strong></td><td ><Strong>Frequency</Strong></td></tr> <tr><td> <div class="rtc-setting-row ddlZoom"> </div></td><td><div class="rtc-setting-row ddlFrequency"></div> </td> </tr>';
        }
        if (UIConfig.pCtrl2.showChartType) str += '<tr> <td > <strong>Chart Cursor</strong> </td> <td > <strong>Chart Type</strong> </td> </tr> <tr> <td > <div class="rtc-setting-row ddlCursorType"></div></td><td> <div class="rtc-setting-row ddlChartType"></div> </td> </tr>';
        if (UIConfig.pCtrl2.showIndicator) str += '<tr><td colspan="2"><Strong>Technical Indicators</Strong></td></tr> <tr><td colspan ="2" class="rtc-setting-row"> <ul><li menuitemstyle="item" idkey="indicatorList" class="rtc-pmtxt" style="width:150px" ><a href="javascript:void(0)" mname="indicator">Indicator</a><ul> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="EMA" name="EMA" catkey="indicator" >EMA</a></li> <li><a href="javascript:void(0)" menuitemstyle="trigger" class="rtq-chkbox rtq-chkbox_off" value="BBands" name="BBands" catkey="indicator">BBands</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="PSAR" name="PSAR" catkey="indicator">PSAR</a></li> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="MACD" name="MACD" catkey="indicator">MACD</a></li> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="RSI" name="RSI" catkey="indicator">RSI</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="ROC" name="ROC" catkey="indicator" >ROC</a></li> <li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="SStochastic" name="SStochastic" catkey="indicator">Slow Stochastic</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="FStochastic" name="FStochastic" catkey="indicator">Fast Stochastic</a></li> <li><a class="rtq-chkbox rtq-chkbox_off" href="javascript:void(0)" menuitemstyle="trigger" value="WillR" name="WillR" catkey="indicator">Williams %R</a></li> </ul> </li></ul></td></tr>';
        str+='<tr><td colspan="2"><Strong>Fundamentals</Strong></td></tr><tr><td colspan ="2" class="rtc-setting-row"><ul><li menuitemstyle="item" idkey="valuationList" class="rtc-pmtxt" style="width:150px" ><a href="javascript:void(0)" mname="valuation">Fundamentals</a><ul idkey="valuationUL"><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="PE" name="" catkey="valuation" >Price/Earnings Ratio</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="PB" name="" catkey="valuation" >Price/Book Ratio</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="PC" name="" catkey="valuation" >Price/Cash Ratio</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="PS" name="" catkey="valuation" >Price/Sales Ratio</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="FairValue" name="" catkey="valuation" >Fair Value</a></li></ul></li></ul></td></tr>';
		
		if (UIConfig.pCtrl2.showSMV) str += '<tr><td colspan="2"><Strong>Price Moving Averages</Strong></td></tr> <tr><td colspan="2"><input type="text" idkey="mov1" style="width: 44px;" class="qs-ui-ipt" /> <input type="text" idkey="mov2" style="width: 44px;" class="qs-ui-ipt" /> <input type="text" idkey="mov3" style="width: 44px;" class="qs-ui-ipt" /></td></tr>';
        str += ' <tr><td colspan="2"><Strong>Volume Moving Average</Strong></td></tr><tr><td colspan="2"><input idkey="movVol" style="width: 44px;" class="qs-ui-ipt" type="text" /></td></tr>';
        if (UIConfig.pCtrl2.showEvent) str += '<tr><td colspan="2"><Strong>Events</Strong></td></tr> <tr><td colspan ="2" class="rtc-setting-row"> <ul> <li menuitemstyle="item" idkey="eventList" class="rtc-pmtxt" style="width:150px" ><a href="javascript:void(0)" mname="event">Event</a><ul idkey="eventUL"> <li><a class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="dividend" name="dividends" catkey="event" href="javascript:void(0)">Dividend</a></li><li><a href="javascript:void(0)" class="rtq-chkbox rtq-chkbox_off" menuitemstyle="trigger" value="splits" name="splits" catkey="event">Splits</a></li> </ul> </li> </ul></td></tr>';
        str+='</table>';
        $settingPanel.html(str);
    }
	function createTab(){
		    tabMenu = new Util.TabMenu($('.rtc-tab',outObj)[0],tabList, {
            onActive: function(tab){
				var inx=tab.index; 
				var v = inx==0?"lines":(inx==1?"ohlc":"candle");                
				ddlChartType.selectItemByValue(v);
            }
        },{ width:80,
           isExcuteCallbackAtInit:false
        });	
	}
    this.autoboxCallback = function(line){
		validateCompairTicker(line);
	}
    this.initUIEvents = function () {  
        if (chartCursor==1) {  // chart cursor mode is crosshair
            crosshairLegendElmContainer.bind("mouseover",function(){ $(this).show(); clearTimeout(timeout);timeout=null; }) ;
            lineXElm.bind("mouseover",function(){$(this).show();clearTimeout(timeout);timeout=null;}) ;
            lineYElm.bind("mouseover",function(){$(this).show();clearTimeout(timeout);timeout=null;}) ;
        }
		legendElm.unbind();
		legendElm.bind("mouseover.rtqChart",function(e){
			if (e.target.tagName.toUpperCase() == "SPAN") {
				var elm = $(e.target).closest(".rtc-legend");
				legendElm.find(".rtc-legend-remove").removeClass("rtc-legend-remove");
				elm.addClass("rtc-legend-remove");
			}else{
				legendElm.find(".rtc-legend-remove").removeClass("rtc-legend-remove");
			}
		});
		legendElm.bind("mouseout.rtqChart",function(e){
			legendElm.find(".rtc-legend-remove").removeClass("rtc-legend-remove");
		});
		legendElm.bind("click.rtqChart",function(e){
			if (e.target.tagName.toUpperCase() == "SPAN") {
				var elm = $(e.target).closest(".rtc-legend");
				legendOnClick(elm);
			}
		});
        if (UIConfig.pCtrl1 && UIConfig.pCtrl1.showCompair && $("#"+autoBoxID).length > 0) {  // init compair section
			Util.createAutoCompleteBox(autoBoxID,downlistID,"USA","EN-US",8,true,objName+".autoboxCallback");
			if (UIConfig.chartCtrl != "default") {
				$("#"+downlistID).css({ left:"10px", top: "56px"});
			}
            $("#"+autoBoxID).val("Enter Security");
            $("#"+autoBoxID).css({color:"#666"});
            $("#"+autoBoxID).click(function(){clearBox(this);});
            addCompairBtElm.unbind("click");
            addCompairBtElm.click(function(){addSecurity();});
        }
        if (UIConfig.pCtrl1&&UIConfig.pCtrl1.showTimeRange) { // init custom date input box.
            chartObj.initCustomTimeRangeBox(startDate,endDate); 
        }
        if (UIConfig.pCtrl2&&UIConfig.pCtrl2.showIndicator) { 
            var $bt = indicatorWinElm.find("[idkey='drawBt']");  
            $bt.unbind("click"); $bt.bind("click",function(){drawIndicator(this);});
            var $ct = indicatorWinElm.find("[idkey='cancelBt']"); $ct.unbind("click"); $ct.bind("click",function(){cancelIndicator(this);}); 
            var $rt = indicatorWinElm.find("[idkey='removeBt']"); $rt.unbind("click"); $rt.bind("click",function(){removeIndicatorClicked(this);});
            var $closet = indicatorWinElm.find("[idkey='close']"); $closet.unbind("click"); $closet.bind("click",function(){cancelIndicator(this);});   
        }                        
        if (UIConfig.RightMenu){ 
            if (typeof(SimpleContextMenu) != "undefined") { 
                SimpleContextMenu.setup({'preventDefault':false, 'preventForms':false});
                SimpleContextMenu.attach(outContainerId+'-rmenu', outContainerId+'_CM1',[lineXElm,lineYElm,crosshairLegendElmContainer]); 
            }
            chartObj.attachMenuEvent(rightMenuElm);    // attach events to right menu.
        }  
        if (UIConfig.vCtrl != null && UIConfig.vCtrl.showHideLink) { 
            outObj.find("[idkey='volLink']").unbind("click");
            outObj.find("[idkey='volLink']").bind("click",function(){toggleVolumeChartVision();return false;});// hide or show volume chart.
        }          
        if (UIConfig.chartCtrl == "rtqPanel") {
            chartObj.attachMenuEvent($settingPanel); //ctrl UI is in $setting panel
        } else {
            chartObj.attachMenuEvent(outObj);
        }
        if (UIConfig.pCtrl2 != null && UIConfig.pCtrl2.showSMV) { // init smv section.
            m1.css({color:"#666"});m2.css({color:"#666"});m3.css({color:"#666"});
            m1.unbind("keydown");m2.unbind("keydown");m3.unbind("keydown");
            m1.bind("keydown",function(kp){var keyID = (window.event) ? kp.keyCode : kp.which ? kp.which : kp.charCode;if(keyID==13) addSMV(0,m1.attr("value"));});
            m2.bind("keydown",function(kp){var keyID = (window.event) ? kp.keyCode : kp.which ? kp.which : kp.charCode;if(keyID==13) addSMV(1,m2.attr("value"));});
            m3.bind("keydown",function(kp){var keyID = (window.event) ? kp.keyCode : kp.which ? kp.which : kp.charCode;if(keyID==13) addSMV(2,m3.attr("value"));});     
            m1.click(function(){clearBox(this);});m2.click(function(){clearBox(this);});m3.click(function(){clearBox(this);});
        }
        if (UIConfig.vCtrl != null && UIConfig.vCtrl.showSMVV) {   // init smvv section.
            mv.css({color:"#666"}); mv.unbind("keydown");mv.click(function(){clearBox(this);});
            mv.bind("keydown",function(kp){var keyID = (window.event) ? kp.keyCode : kp.which ? kp.which : kp.charCode;if(keyID==13) addSMVV(mv.attr("value"));});
        }
        if ((UIConfig.Chart != null && UIConfig.Chart.showTimeSlider) || UIConfig.CustomUI){   //init slider section
            createSlider();                // create slider object.
        }
		//trend line jacky
		$(".rtq-chart-add-trendline").bind("click",function(){
			$(".rtq-chart-add-trendline").attr("disabled","disabled");
			p.trendLineAble = true;
		});
        if(ddlZoom != null) ddlZoom.setDisable(false);
        if(ddlChartType != null) ddlChartType.setDisable(false);
        if(ddlCursorType != null) ddlCursorType.setDisable(false);
        if(ddlFrequency != null) ddlFrequency.setDisable(false);
		if(ddlScaleType != null) ddlScaleType.setDisable(false);
    };
        
    this.attachMenuEvent = function(obj) { 
        if (obj == null || obj.length < 1) {
            return;
        }
        obj.find("[menuItemStyle='item']").unbind("hover");
        obj.find("[menuItemStyle='item']").hover(function() { changeClassName(this, 'over'); }, function() { changeClassName(this, 'out'); });
        obj.find("[menuItemStyle='subMenu']").unbind("hover");
        obj.find("[menuItemStyle='subMenu']").hover(function() { changeStyle(this, 'display'); }, function() { changeStyle(this, 'none'); });
        obj.find("[menuItemStyle='trigger']").unbind("click");
        obj.find("[menuItemStyle='trigger']").click(function() {menuButtonClicked(this);});
    };
    function initDropDownList() {
        if (UIConfig.pCtrl1 && UIConfig.pCtrl1.showZoom && ddlZoom == null) {
            ddlZoom = new Util.DropDownList($ddlZoom[0], zoomSelected, zoomListData, {triggerByClick:false, width:75,disable:true,actionAtInit:false});
        }
        if (UIConfig.pCtrl1 && UIConfig.pCtrl1.showFrequency && ddlFrequency == null) {
            ddlFrequency = new Util.DropDownList($ddlFrequency[0], frequencySelected, frequencyListData, {triggerByClick:false, width:75,disable:true, actionAtInit:false});
        }
        if (UIConfig.chartCtrl == "rtqPanel" && UIConfig.pCtrl2 && UIConfig.pCtrl2.showCursorType && ddlCursorType == null) {
            ddlCursorType = new Util.DropDownList($ddlCursorType[0], cursorTypeSelected, cursorTypeListData, {triggerByClick:false, width:75,disable:true, actionAtInit:false});
        }
        if (UIConfig.pCtrl2 && UIConfig.pCtrl2.showChartType && ddlChartType == null) {
            ddlChartType = new Util.DropDownList($ddlChartType[0], chartTypeSelected, chartTypeListData, {triggerByClick:false, width:75, disable:true,actionAtInit:false});
        }
		if(UIConfig.chartCtrl == "rtqPanel"){
			createTab();
			ddlScaleType = new Util.DropDownList($ddlScaleType[0], scaleTypeSelected, scaleTypeListData, {triggerByClick:false, width:150, disable:true,actionAtInit:false});
		}
    }    
    function initConfig(option)  { // init the chart's configuration
        var cofig;
        if (option == null || option == "") {
            return;
        }
        config = option; //eval("("+option+")");
        if (config != null) {
            initProfileConfig(config);
        }
        var ih = getIndicatorChartHeight();
		var valuationChartH = getValuationChartHeight();
        if (config.width != null) {
            pChartWidth = (config.width>minW)?config.width:minW; 
            vChartWidth = (config.width>minW)?pChartWidth:minW; 
            miniChartWidth=(config.width>minW)?pChartWidth:minW;
        }
        if (config.height != null) {
            var h = config.height-38-ih-valuationChartH-36-30;  var yTop = 34;
            if (UIConfig.chartCtrl == "default") {
                if (UIConfig.pCtrl1 != null ) {
                    h -= 28; yTop += 28;
                }
                if (UIConfig.pCtrl2 != null ) {
                    h -= 28; yTop += 28;
                }
                if (UIConfig.vCtrl != null) {
                    h -= 28;
                }
                //lineYElm.css({top:yTop+"px"});
            } else if (UIConfig.chartCtrl == "rtqPanel") {
                h -= 40;
            }
            pChartHeight = (h>160)?h*0.8:110;
            vChartHeight =(h>160)?h*0.2:50;
            outObj.css({height:config.height-30+"px"});
            outHeight = config.height-30;
        }  
    }
    
    function getIndicatorChartHeight() {
        var ih = 0;
        for (var o in indicatorVisiable) { 
            if (indicatorVisiable[o] && o != "EMA" && o != "BBands" && o != "PSAR") {
                ih+=76;
            }   
        }
        return ih;
    }
    function getValuationChartHeight(){
		var ih = 0;
        for (var o in valuations) { 
            if (valuations[o].show) {
                ih+=76;
            }   
        }
        return ih;
	}
    function initProfileConfig(cf)  { // init chart's prameters by user's profile.
        if (cf == null) return;
        if (cf.mTicker != null) mainTicker = cf.mTicker; if (cf.cTickers != null) compairTickers = cf.cTickers;
        if (cf.pWidth != null) pChartWidth=cf.pWidth; if (cf.vWidth != null) vChartWidth = cf.vWidth;
        if (cf.pHeight != null) pChartHeight=cf.pHeight; if (cf.vHeight != null) vChartHeight = cf.vHeight;
        if (cf.mWidth != null)miniChartWidth = cf.mWidth; if (cf.mHeight != null) miniChartHeight = cf.mHeight;
        if (cf.isD != null) isDividend = cf.isD;  if (cf.isS != null) isSplits = cf.isS;
        if (cf.zRange != null) zoomRange = cf.zRange; if (cf.startDate != null) startDate = convertToDate(cf.startDate);
        if (cf.endDate != null) endDate = convertToDate(cf.endDate); if( cf.region != null) region = cf.region;
        if (cf.lan != null) lan = cf.lan; if (cf.cur != null) cur=cf.cur;
        if (cf.frequency != null) frequency = cf.frequency; if (cf.needGetVolume != null) needGetVolume = cf.needGetVolume;
        if (cf.latestTradeDate != null) latestTradeDate = convertToDate(cf.latestTradeDate); if (cf.slider_EndDate != null) slider_EndDate = convertToDate(cf.slider_EndDate);
        if (cf.slider_StartDate != null) slider_StartDate = convertToDate(cf.slider_StartDate); if (cf.timeRangeType != null) timeRangeType = cf.timeRangeType;
        if (cf.miniChartFrequency != null) miniChartFrequency = cf.miniChartFrequency; if (cf.needDrawMiniChart != null) needDrawMiniChart = cf.needDrawMiniChart;
        if (cf.needDrawMiniChart != null) needDrawMiniChart = cf.needDrawMiniChart; if (cf.dateFromFocus != null) dateFromFocus = cf.dateFromFocus;
        if (cf.dateToFocus != null) dateToFocus = cf.dateToFocus; if (cf.chartStyle != null) chartStyle = cf.chartStyle;
        if (cf.showVolume != null) showVolume = cf.showVolume;
        if (mainTicker != null) SecurityData[mainTicker] = new quoteData();
        if (compairTickers != null && compairTickers.length > 0){
            for (var i=0; i<compairTickers.length; i++) SecurityData[compairTickers[i]] = new quoteData();
        }
        if (cf.tickerInfos) tickerInfos = cf.tickerInfos;
        if (UIConfig.chartCtrl == "default") lin.html((showVolume?"Hide":"Show"));
        if (cf.indicatorVisiable != null) indicatorVisiable = cf.indicatorVisiable;
        if (cf.chartCursor != null) chartCursor = cf.chartCursor;
        if (cf.indicatorsData)indicatorsData = cf.indicatorsData;
		if(cf.indexsMap)indexsMap = cf.indexsMap;
		if(cf.valuations)valuations = cf.valuations;
		if(cf.scaleType)scaleType = cf.scaleType;
		if (typeof(cf.isF)!= "undefined" && cf.isF != null)isF = cf.isF;
		// change the stored old data formate to new;
		if(!cf.smvV && !indicatorsData["SMAV"]){
			indicatorsData["SMAV"] = {data:null,num:null,startPos:null,colorIndex:0,type:0};
		}else if(!indicatorsData["SMAV"]){
			indicatorsData["SMAV"] = {data:null,num:cf.smvV,startPos:null,colorIndex:0,type:0};
		}
		if((!cf.smvP || !cf.smvP.length) && !indicatorsData["SMA"]){
			indicatorsData["SMA"] = {data:[null,null,null],num:[null,null,null],startPos:[null,null,null],colorIndex:[0,0,0],type:0};
		}else if(cf.smvP && cf.smvP.length){
			indicatorsData["SMA"] = {data:[null,null,null],num:[cf.smvP[0]?cf.smvP[0]:null,cf.smvP[1]?cf.smvP[1]:null,cf.smvP[2]?cf.smvP[2]:null],startPos:[null,null,null],colorIndex:[0,0,0],type:0};
		}
		if(indicatorsData.EMA.length){   // old structure;
			var ema  = {data:[null,null,null],num:[null,null,null],startPos:[null,null,null],colorIndex:[0,0,0],type:0};
			for(var i=0; i< indicatorsData.EMA.length;i++){
				ema.num[i]= indicatorsData.EMA[i].num;
				ema.startPos[i]= indicatorsData.EMA[i].startPos;
				ema.colorIndex[i]= indicatorsData.EMA[i].colorIndex;
			}
			indicatorsData.EMA = ema;
		}
		
		var smaNums  = indicatorsData["SMA"]["num"];
		if(smaNums){
			smaNums[0]== null?"":m1.val(smaNums[0]);
			smaNums[1]== null?"":m2.val(smaNums[1]);
			smaNums[2]== null?"":m3.val(smaNums[2]);
		}
		indicatorsData["SMAV"]["num"]==null?"":mv.val(indicatorsData["SMAV"]["num"]);
     }
     
    function bindUIstyle() {
        if (rightMenuElm != null && rightMenuElm.length > 0) {
            if(isDividend) rightMenuElm.find("a[value='dividend']").removeClass("rtq-chkbox_off");
            if(isSplits) rightMenuElm.find("a[value='splits']").removeClass("rtq-chkbox_off");
            rightMenuElm.find("ul[idkey='divPlotStyle']").find("a.on").removeClass("on");
            rightMenuElm.find("ul[idkey='divPlotStyle']").find("a[value='"+chartStyle+"']").addClass("on");
            rightMenuElm.find("ul[idkey='divCursorType']").find("a.on").removeClass("on");
            rightMenuElm.find("ul[idkey='divCursorType']").find("a[value='"+chartCursor+"']").addClass("on");
        }
        if(chartCursor != "1" && chartCursor != 1) {
            lineXElm.hide();
            lineYElm.hide();
        }
        if (ddlZoom != null) ddlZoom.setTextValue(null,zoomRange);
        if (ddlFrequency != null) ddlFrequency.setTextValue(null,frequency);
        if (ddlCursorType != null) ddlCursorType.setTextValue(null,chartCursor);
        if (ddlChartType != null) ddlChartType.setTextValue(null,chartStyle);
	    if (ddlScaleType != null) ddlScaleType.setTextValue(null,scaleType);
		if (tabMenu != null){
			var ing = chartStyle=="lines"?0:(chartStyle=="candle"?2:1);
			tabMenu.changeTabByIndex(ing,false);
		}
        var obj = (UIConfig.chartCtrl == "default") ? outObj:$settingPanel;
        var ev = obj.find("li[idkey='eventList']");
        var sel = chartStyleULElm.find("a[value='"+chartStyle+"']");
        if(isDividend) ev.find("a[value='dividend']").removeClass("rtq-chkbox_off");
        if(isSplits) ev.find("a[value='splits']").removeClass("rtq-chkbox_off");
        sel.addClass("on");
        var $cu = chartCursorULElm.find("a[value='"+chartCursor+"']");
        chartCursorULElm.find("a.on").removeClass("on");
        $cu.addClass("on");
        chartCursorElm.html($cu.html());
        chartStyleElm.html(sel.html());
        for (var o in indicatorVisiable) {             // change indicator menu state.
            if (indicatorVisiable.hasOwnProperty(o) ) {
                 if (indicatorVisiable[o]) {
                      obj.find("li[idkey='indicatorList']").find("a[value='"+o+"']").removeClass("rtq-chkbox_off");
                      if (UIConfig.RightMenu) {
                           rightMenuElm.find("li[idkey=='indicatorList']").find("a[value='"+o+"']").removeClass("rtq-chkbox_off");
                      }
                 }
            }
        }
		for(var o in valuations){
			if(valuations[o].show){
				valuationULElm.find("a[value='"+o+"']").removeClass("rtq-chkbox_off");
			}
		}
		for(var o in indexsMap){
			if(indexsMap[o]){
				indexsULElm.find("a[value='"+o+"']").removeClass("rtq-chkbox_off");
			}
		}
    }
     
    function saveConfig() {  // save the chart's configuration.
        var indicatorConfig = {};
        for (var o in indicatorsData){
        	if (indicatorsData.hasOwnProperty(o)) {
				indicatorConfig[o] = {};
				for(var oo in indicatorsData[o]){
					if(oo == "data"){
						if (indicatorsData[o][oo] && indicatorsData[o]["num"]&& indicatorsData[o]["num"].length > 0) {
							indicatorConfig[o][oo] = [];
							for (var i = 0; i < indicatorsData[o][oo].length; i++) {
								indicatorConfig[o][oo][i] = null;
							}
						} else {
							indicatorConfig[o][oo] = null;
						}
					} else {
						indicatorConfig[o][oo] = indicatorsData[o][oo];
					}
				}
            }
        }
        var cf = {mTicker:mainTicker,cTickers:compairTickers,pWidth:pChartWidth+34,vWidth:vChartWidth,pHeight:pChartHeight,
        vHeight:vChartHeight,mWidth:miniChartWidth,mHeight:miniChartHeight,isD:isDividend,isS:isSplits,zRange:zoomRange,startDate:formatDate(startDate),
        endDate:formatDate(endDate),region:region,lan:lan,cur:cur,frequency:frequency,needGetVolume:needGetVolume,latestTradeDate:formatDate(latestTradeDate), 
        slider_EndDate:formatDate(slider_EndDate),slider_StartDate:formatDate(slider_StartDate),
        timeRangeType:timeRangeType,miniChartFrequency:miniChartFrequency,isF:isFairValue,
        needDrawMiniChart:needDrawMiniChart,dateFromFocus:dateFromFocus,dateToFocus:dateToFocus,chartStyle:chartStyle,showVolume:showVolume,
        indicatorVisiable:indicatorVisiable, tickerInfos:tickerInfos, chartCursor:chartCursor,indicatorsData:indicatorConfig,scaleType:scaleType
        };
		if(UIConfig.chartCtrl="rtqPanel"){
			var ext = {valuations:{},indexsMap:indexsMap}
			for(var o in valuations){
				ext.valuations[o] = {};
				ext.valuations[o]["data"]=[];
				ext.valuations[o]["oFlot"]= null;
				for(var oo in valuations[o]){
					if(oo != "data" && oo != "oFlot"){
						ext.valuations[o][oo] = valuations[o][oo];
					}
				}
			}
			$.extend(cf,ext);
			return cf;
		}else {
			return cf;
		}
    }
    
	function storeTickerInfo(d,info){
		var tenforInfo = info.TenforeInfo;
		d["tenforeCode"]= tenforInfo[1];
		d["tenforeTicker"] = tenforInfo[0];
		d["type"] = toStockTypeCode(info.Type);    // sercurity type
		d["performanceId"] = info.PID;
		d["secId"] = info.SID;
		d["exch"] = info.Exch;
		d["ticker"] = info.Ticker;
		d["country"] = info.Region;
		d["currency"] = info.Currency;
		d["companyId"] = info.CFID;
		d["ISIN"] = info.ISIN;
		d["Name"]= info.Name;
		d["gkey"]= (info.Region == "USA"?"126":tenforInfo[1])+":"+tenforInfo[0];
	} 
    this.SelectMainTicker = function(data)  {   // handler check main ticker.
        if (data == null || data.Records == null || data.Records.length == 0) {
            return;
        }
        var tickerList = data.Records;
        if (tickerList == null || tickerList.length < 0) {
            return;
        }
        if (tickerList[0].PID != "" && tickerList[0].PID != mainTicker) {         // use performanceId as ticker key.
            if(mainTicker != ""){
				bacthUnscribeTickers([mainTicker]);
				delete tickerInfos[mainTicker];
			}
			mainTicker = tickerList[0].PID;
			tickerInfos[mainTicker]= {};
			storeTickerInfo(tickerInfos[mainTicker],tickerList[0]);  // store ticker information.
			subscribeStock([mainTicker]);                   // suscribe main ticker.
			initComponent();
			if (chartObj.setTitle) {
                var t = tickerInfos[mainTicker]["ticker"];
                chartObj.setTitle("Chart:"+t);
            }
        } 
		if(typeof(chartObj.afterAcceptTickers) == "function"){
			chartObj.afterAcceptTickers({mainTicker:tickerInfos[mainTicker]});
		}
    };
    this.SecruritySelected = function (data) {   // add compair tickers.
        if (data == null || data.Records == null || data.Records.length == 0) {
            return;
        }
        var tickerList = data.Records;
        var flag = false;
		var symbols =[], needSubscribe = [];
        for (var i = 0; i < tickerList.length; i++) {
    	    var ticker = tickerList[i];	
    		if (ticker.PID == "" || checkCompairTicker(ticker.PID)) {
    		    continue;
    		} else {
    		    if (compairTickers.length >= tickerMaxNum) {
    		        if (hasConfirmBox) {
                        Util.confirm("The maximum compair tickers are reached.",function(bFlg){return;});                                                                                              
                    } else {
                        break;
                    }  
                   break;
    			}	
			    compairTickers.push(ticker.PID);
				tickerInfos[ticker.PID] = {};
				storeTickerInfo(tickerInfos[ticker.PID],tickerList[i]);	
				SecurityData[ticker.PID] = new quoteData();		
				// subscribe compair tickers.
				
				requestIntrayData(ticker.PID,5);
    		    flag = true;
				needSubscribe.push(ticker.PID);
				symbols.push(tickerInfos[ticker.PID]);
				if(indexsMap[ticker.PID]){
					indexsULElm.find("a[value='"+ticker.PID+"']").removeClass("rtq-chkbox_off");
				}
    		}
    	}
		if(needSubscribe.length > 0){   // subscribe compair tickers.
			subscribeStock(needSubscribe);
		}
		if(typeof(chartObj.afterAddTickers) == "function"){
			chartObj.afterAddTickers({symbol:symbols});
		}
    	if (flag && zoomRange > 2) { 
    	    Draw(false);
    	}	
		
    };
    function initStartDate() {
        dailyStartDate = new Date(latestTradeDate.getFullYear()-2,0,1);
        weeklyStartDate = new Date(latestTradeDate.getFullYear()-20,0,1);
        monthlyStartDate = new Date(1900,0,1);
        slider_StartDate = getSliderStartDate();  
    } 
    
    function buildCpTickerString() {
        return (compairTickers != null && compairTickers.length > 0)?compairTickers.join(","):""; 
    }
     
    function buildHisDataRequestUrl(ticker,isGetVolume,fre) {
        var st;
        var f = (fre=="minute")?"d":fre;
        if (f =="d") {
            st = dailyStartDate;
        } else if(f =="w") {
            st = weeklyStartDate;
        } else if(f == "m") {
            st = monthlyStartDate;
        } else {
            st = dailyStartDate;
        }
        var rang = formatDate(st)+"|"+formatDate(latestTradeDate);     
        var urlC = hisUrl+"ticker=" + ticker+ "&showVol=" + isGetVolume;
        urlC+= "&dtype=his&f="+f+"&curry=" + cur + "&range=" + rang + "&lan=" + lan + "&region=" + region;
        var ls = ticker.split(",");
        if (ls[0].toUpperCase() == mainTicker.toUpperCase()) {
            urlC+="&isD=true&isS=true";
			if(UIConfig.fairValue){
				urlC+="&isF=true";
			}
        }
		
        urlC+="&jsoncallback="+objName+".processData";
        return urlC;
    } 
    function getPidByExchAndTicker(symbol){  // get PID 
		var t = symbol.split(":");
		if(t.length < 2){
			if(mainTicker == symbol){
				return symbol;
			}else{
				for(var i=0;i<compairTickers.length; i++){
					if(compairTickers[i]==symbol){
						return compairTickers[i];
					}
				}
			}
		} else if(typeof(tickerInfos) == "object"){
			for(var o in tickerInfos){
				if(tickerInfos[o]["gkey"]== symbol||(tickerInfos[o].exch == t[0]&&tickerInfos[o].ticker == t[1])){
					return o;
				}
			}
		}
	}
	function filterValucationData(sName){
	   var stIndx = toDateIndex(startDate);
       var endInx = toDateIndex(endDate);
	   if(zoomRange <= 2){//intraday 
	   		valuations[sName].data = [];
	   } else if (SecurityData[mainTicker]) {
			var d = SecurityData[mainTicker].getDataByType(frequency);
			valuations[sName].data = d.DataFilter(stIndx,endInx,true,sName,1);
	   }
	}
	function drawValuations(){
		for(var o in valuations){
			if(valuations[o].show){
				drawValuation(o);
			}
		}
	}
	function drawValuation(sName){
		var field = checkValuationData(frequency,sName);
		if(field == -1){return}	;
		if(field == "" || zoomRange <= 2){
			filterValucationData(sName);
			drawValuationChart(sName);
		} else {
			requestValuationData(mainTicker,field,frequency);
		}
		
	}
	function requestValuationData(symbols,fields,f){
        if (f =="d") {
            st = dailyStartDate;
        } else if(f =="w") {
            st = weeklyStartDate;
        } else if(f == "m") {
            st = monthlyStartDate;
        } else {
            st = dailyStartDate;
        }
        var st = formatDate(st);
		var ed = formatDate(latestTradeDate);     
        var urlC = "getFundamentalData.jsp?symbols=" +symbols+ "&fileds=" + fields;
        urlC+= "&f="+f+"&st=" + st + "&ed=" + ed+"&sId="+Cookies.get("SessionID");
        $.get(urlC,processValuationData);
	}
	function checkValuationData(f,field){
		var d = SecurityData[mainTicker];
		if(!d){
			return -1;
		}
		var dd = d.getDataByType(f);
		var fields = "";
		if (!field) {
			for (var o in valuations) {
				if (valuations[o].show) {
					if (dd && dd.ValuationData && dd.ValuationData[o].length < 1) {
						fields += valuations[o]["code"] + ",";
					}
				}
			}
		} else {
			if(dd && dd.ValuationData && dd.ValuationData[field].length < 1){
				fields += valuations[field]["code"];
			}
		}
		return fields;
	}
	function processValuationData(data){
		var d = eval("("+data+")");
		if(!d.data || d.data.length < 1)return;
		var dd = d.data;
		var sData,flist,sName;
		for(var i = 0; i<dd.length;i++){
			if(dd[i].symbol && dd[i].symbol == mainTicker){
				if(SecurityData[dd[i].symbol]){
					sData = SecurityData[dd[i].symbol].getDataByType(frequency);
				}
				if(!sData || !sData.ValuationData || !dd[i].field ||dd[i].field.length < 1) continue;
				for(var j = 0; j < dd[i].field.length;j++){ // store the valuation Data.
					flist = dd[i].field[j];
					sName = mapValuationName[flist.name];
					SecurityData[dd[i].symbol].saveValuationData(flist.value,flist.date,frequency,sName);
					if(zoomRange > 2){
						filterValucationData(sName);
						drawValuationChart(sName);
					}
				}
			}
		}
		
	}
    function requestHisData(ticker,isGetVolume,f) { 
        var  url = buildHisDataRequestUrl(ticker,isGetVolume,f);
        $.getScript(url); 
    }
    
    function  processData(data) { // receive historical data from server.
        if (data ==null) {
            return;
        }
        var priceData = data.PriceDataList;
        var divdendData = data.DividendData;
        var f = (frequency=="minute")?"d":frequency;
        if (needDrawMiniChart) f = miniChartFrequency; // request mini chart data.
        if (priceData == null || priceData.length<1) {
            return;
        }
        var ins = priceData[0].DateIndexs;
        for (var i = 0; i < priceData.length; i++) {  
            var symbol = getPidByExchAndTicker(priceData[i].Symbol);
            var dataList = priceData[i].Datapoints;
            var sPos = (priceData.length>1?priceData[i].StartPos:NaN);
            var volList = null, fList = null;
            if( i == 0 && data.VolumeList != null) {
                volList = data.VolumeList.Datapoints;
            }
			if(i == 0 && data.FairValueList != null){
				fList = data.FairValueList.Datapoints;
			}
            storeData(dataList,volList,fList,ins,f,symbol,sPos);
        }  
        if (divdendData != null && divdendData.length > 0 && getPidByExchAndTicker(priceData[0].Symbol) == mainTicker) {
            storeDividendData(divdendData,mainTicker);
        }  
        if (timeRangeType != "oneday" && timeRangeType != "fivedays" && !needDrawMiniChart) {
            setTimeout(function(){drawHisChart();drawIndicatorChart();drawValuations();},200);
        }
        if(needDrawMiniChart) {  // need to draw mini chart
            filterMiniChartData(); 
            drawMiniChart(); 
            needDrawMiniChart= false; 
            chartObj.setSliderHandleValues();
        } 
    }
    
    function storeDividendData(dlist,symbol) {   // store dividend and splits data.
        if (SecurityData[symbol] == null) {
            SecurityData[symbol] = new quoteData();
        }
        var da = SecurityData[symbol].dividendAndSplitData;
        var st = 0 , ls, dt, dIndex;
        if (da.StartIndex != null) st = da.StartIndex;
        for(var i = 0; i < dlist.length; i++) {    
            ls = dlist[i].Date.split("-");
            dt = new Date(parseInt(ls[0],10),parseInt(ls[1],10)-1,parseInt(ls[2],10));
            dIndex = toDateIndex(dt);
            if (i==0) { 
                if (st != 0 && dIndex >= st) {
                    break;
                } else { 
                    da.StartIndex = dIndex;
                    da.Data = [];
                } 
            }
            if (i == dlist.length-1) {
                da.EndIndex = dIndex;
            }
            da.Data.push({DateIndex:dIndex,Desc:dlist[i].Desc,Type:(dlist[i].Type=="Dividend"?1:0)});
        }
    }
    
    function storeData(plist,vlist,flist,inxlist,freq,symbol,spos) {  // store market price data on the client
        if (SecurityData[symbol] == null) {
            SecurityData[symbol] = new quoteData()
        }
        SecurityData[symbol].saveTimeSeriesData(plist,vlist,flist,inxlist,freq,spos);
    }
    
    // Draw Chart 
    function Draw(bIsDrawIndicator) { 
        pChartReady = false;
        var tickers="";
        if (timeRangeType =="oneday" || timeRangeType == "fivedays") {
            frequency = "minute";
            filterIntradeData(true); 
            drawChart(priceChartContainer,DataList);
            setTimeout(function(){drawVol(volChartContainer,VolList);},500);
        } else {
            tickers = CheckData();
            tickers = tickers.substr(0,tickers.length-1);
            if (tickers != "") {
                requestHisData(tickers,needGetVolume,frequency);
            } else {
                drawHisChart();
            }
        }
        if (bIsDrawIndicator) { 
            if (tickers == "" || tickers.split(",")[0] != mainTicker) {
                drawIndicatorChart(); 
				drawValuations();
            }   
        }
    }
    
    function drawHisChart(bIsDrawPriceChart) {
        filterHistoricalData();
        drawChart(priceChartContainer,DataList);
        setTimeout(function(){ drawVol(volChartContainer,VolList);},300);
    }
    
    function DrawMiniChart() { 
        if (UIConfig.Chart == null || (!UIConfig.Chart.showTimeSlider&& !UIConfig.CustomUI)) {
            return;
        }
        var isDataReady = true;
        if (mainTicker != null && SecurityData[mainTicker] == null) {
            isDataReady = false;
        } else { 
            var aData = SecurityData[mainTicker].getDataByType(miniChartFrequency);
            if (aData == null || aData.Data.length < 1) {
                isDataReady = false;
            }    
        }
       if (isDataReady) {
         filterMiniChartData();
         drawMiniChart();
       } else { 
         needDrawMiniChart = true;
         requestHisData(mainTicker,true,miniChartFrequency);
       }   
    }
    
    function CheckData() { // make sure all data needed in drawing chart is ready.
        var tickers = ""; needGetVolume = false;
        if (mainTicker != null && SecurityData[mainTicker] == null) {
            tickers+=mainTicker+",";
        } else { 
            var aMainData = SecurityData[mainTicker].getDataByType(frequency);
            if (aMainData == null || aMainData.Data.length<1) {
                tickers+=mainTicker+",";
            }
       }
       if (tickers != "") {  // need to get main ticker's volume.
           needGetVolume = true; 
       }
       for(var i=0;i < compairTickers.length; i++) {
           if (SecurityData[compairTickers[i]] == null) {
               tickers+=compairTickers[i]+",";
           } else {
               var aCompairData = SecurityData[compairTickers[i]].getDataByType(frequency);
               if (aCompairData == null || aCompairData.Data.length < 1) {
                    tickers+=compairTickers[i]+",";
               }     
           }
       }
       return tickers;
    }
    
    function filterMiniChartData() {  
        var mainData = SecurityData[mainTicker].getDataByType(miniChartFrequency);
        var d= mainData.DataFilter(mainData.StartIndex,mainData.EndIndex,false,"p",5);
        if (d != null) { 
            slider_EndDate = toDateFromIndex(d[d.length-1][d[d.length-1].length-1]);
            slider_StartDate = toDateFromIndex(d[0][d[0].length-1]);
        }
        MiniChartData = [];
        MiniChartData.push(d);
    }
    
    function filterIntradeData(needfilterData) {
        var s = new Date();
        var mainCopyedData = null; var colorIndex = 0; 
        var bNeedCalReturn = false;
        if (needfilterData) {   //need to filtrade price data and volume data.
            var num=(timeRangeType=="oneday")?1:5;
            DataList = []; 
            var ty = "price";  var spos ;
            if (compairTickers != null && compairTickers.length > 0) {
                bNeedCalReturn = true;
            }
            var mainData = (SecurityData[mainTicker] == null)?null:SecurityData[mainTicker].intradyDayData;
            if (mainData != null) { 
				var lg = tickerInfos[mainTicker].exch+":"+tickerInfos[mainTicker].ticker;
                mainCopyedData = mainData.CopyIntradePriceData(ty,num) ;
                spos = (num==1?mainData.LatestYestClose:mainData.HisYestClose[num-2]);
                DataList.push({data:mainCopyedData,startPos:spos,colorIndex:0,color:tickerColors[0],name:mainTicker,legend:lg}); 
                colorIndex++;
                VolList = mainData.CalculateVolumeData(num);
            }
            if (compairTickers != null) {
                var comData, comCopyedData;
                for (var i = 0; i < compairTickers.length; i++) {
					 var leg = tickerInfos[compairTickers[i]].exch+":"+tickerInfos[compairTickers[i]].ticker;
                     comData = SecurityData[compairTickers[i]].intradyDayData;
                     comCopyedData = comData.CopyIntradePriceData(ty,num);
                     spos = (num==1?comData.LatestYestClose:comData.HisYestClose[num-2]);
                     DataList.push({data:comCopyedData,startPos:spos,colorIndex:i+1,color:tickerColors[i+1],name:compairTickers[i],legend:leg});           
                     colorIndex++;
                }
            }
            if (VolList != null && VolList.length > 0) { 
                ticksize = (timeRangeType=="oneday")?60:VolList.length/5; Ticks=null;
            }
            if (timeRangeType == "fivedays") {
                startDate = mainData.HisDate[mainData.HisDate.length-1];
            } else {
                startDate = endDate;
            }
        } else { 
            mainCopyedData = DataList[0].data; colorIndex = DataList.length;
        }
        filterIndicatorData(0,0,mainCopyedData,0,true,colorIndex,VolList); // filtrate indicator data 
        if (bNeedCalReturn) {
            calculateReturn();
        }
        setTimeRangeBox(startDate,endDate);
        var e = new Date();
        writeTimeLog("filter intraday chart data ",s,e);
    }
    
    function filterHistoricalData() { 
        var s = new Date();
        if (SecurityData[mainTicker] == null || timeRangeType=="oneday"||timeRangeType=="fivedays") {
            return;
        }
       DataList =[];
       var colorIndex = 0;
       var stIndx = toDateIndex(startDate);
       var endInx = toDateIndex(endDate);
       var ty = 1;
       var mainData = SecurityData[mainTicker].getDataByType(frequency);
       if (frequency == "d") {
           ty = 1;
       } else if (frequency == "w") {
           ty = 2;
       } else if (frequency == "m") { 
           ty = 3;
       }
       var dd = (mainData==null?null:mainData.DataFilter(stIndx,endInx,false,"p",1));
       var vd = (mainData==null?null:mainData.DataFilter(stIndx,endInx,false,"v",1));
       if (dd != null) { 
	   	   var lg = tickerInfos[mainTicker].exch+":"+tickerInfos[mainTicker].ticker;
           DataList.push({data:dd,startPos:dd[0][1],colorIndex:colorIndex,color:tickerColors[colorIndex],name:mainTicker,legend:lg}); 
           colorIndex++;
       }
       if (vd != null) {
           VolList = vd;
       }
       if(dd != null && dd.length > 0) {   // change the start and end index to filter compaire his data
           stIndx = dd[0][dd[0].length-1]; 
           endInx = dd[dd.length-1][dd[dd.length-1].length-1];
       } 
       for (var i = 0; i < compairTickers.length; i++) { 
           var comData  =  SecurityData[compairTickers[i]].getDataByType(frequency);
           var cdd = (comData == null?null:comData.DataFilter(stIndx,endInx,true,"p",1));
		   var leg = tickerInfos[compairTickers[i]].exch+":"+tickerInfos[compairTickers[i]].ticker;
           if(cdd != null) {
               DataList.push({data:cdd,startPos:cdd[0][1],colorIndex:colorIndex,color:tickerColors[colorIndex],name:compairTickers[i],legend:leg}); 
               colorIndex++;
           }
       }
	   if(isFairValue && compairTickers.length == 0){       // fair value;
	   		var fairVal = (mainData==null?null:mainData.DataFilter(stIndx,endInx,false,"f",1));
			if(fairVal!=null)DataList.push({data:fairVal,startPos:fairVal[0][1],colorIndex:16,name:"FairValue",legend:"Fair Value"});
	   }
       if (isDividend || isSplits) {
           var dData = SecurityData[mainTicker].dividendAndSplitData; 
           var divData = dData.DividendFilter(stIndx,endInx,"p",isDividend,isSplits);
           if(compairTickers.length <= 0) {
              convertDividendData(divData,dd);
           }
       }
       filterIndicatorData(stIndx,endInx,mainData.Data,ty,false,colorIndex,mainData.VolumeData);
       if (compairTickers.length > 0) {
           calculateReturn();
           convertDividendData(divData,DataList[0].data); // make sure the dividend image is on the price line.
       }
       startDate = toDateFromIndex(stIndx);
       endDate = toDateFromIndex(endInx);
       setTimeRangeBox(startDate,endDate);
       var e = new Date();
       writeTimeLog("filter chart data ",s,e);
    }
    
    function filterIndicatorData(stIndx,endInx,aData,ty,isIntray,colorIndex,aVData) { 
        if (indicatorCal == null) { 
            indicatorCal = new indicatorCalculation();
        }
        if (UIConfig.Chart.showVChart) {
            if (indicatorsData.SMAV != null && indicatorsData.SMAV.num != null) {
                var aSmvv = indicatorCal.calculateSma(aVData,ty+10,indicatorsData.SMAV.num);
                indicatorsData.SMAV.data = isIntray?aSmvv:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aSmvv,ty+10);
                indicatorsData.SMAV.colorIndex = 1;
            }
        }
		var aSmv, d;// = indicatorsData.SMA.data;
        for (var j = 0; j < indicatorsData.SMA.num.length; j++) {
            if (indicatorsData.SMA.num[j] != null) { 
                aSmv = indicatorCal.calculateSma(aData,ty,indicatorsData.SMA.num[j]);
                d = isIntray?aSmv:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aSmv,ty);
				var s ;//
                if (d == null) {
					s = null;
                    //indicatorsData.SMA.startPos[j] = null;
                } else {
					s = isIntray?d[indicatorsData.SMA.num[j]-1][1]:d[j][0][1];
                    //indicatorsData.SMA.startPos[j] = isIntray?d[j][indicatorsData.SMA.num[j]-1][1]:d[j][0][1];
                }
				DataList.push({data:d,startPos:s,colorIndex:colorIndex,color:tickerColors[colorIndex],legend:"SMA("+indicatorsData.SMA.num[j]+")",name:"SMA",sq:j});
                //indicatorsData.SMA.colorIndex[j] = colorIndex;
                colorIndex++;
            }
        }
        if (indicatorVisiable.EMA) { 
            var lg = indicatorsData.EMA.num.length, d; 
            for (var k = 0; k< lg; k++) {
                if (indicatorsData.EMA.num[k] != null) { 
                    var aEma = indicatorCal.calculateEma(aData,ty,indicatorsData.EMA.num[k]);
                    d = ( isIntray?aEma:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aEma,ty));
                    //indicatorsData.EMA.colorIndex[k] = colorIndex;
					DataList.push({data:d,startPos:null,colorIndex:colorIndex,color:tickerColors[colorIndex],legend:"EMA("+indicatorsData.EMA.num[k]+")",name:"EMA",sq:k,num:indicatorsData.EMA.num[k]});
                    colorIndex++;
                }
            }
        }
        if (indicatorVisiable.BBands) {
			var d1, d2;
            var aBBands = indicatorCal.calculateBBands(aData,ty,indicatorsData.BBands.parameter[0],indicatorsData.BBands.parameter[1]);
            //indicatorsData.BBands.data = [];
            d1 = ( isIntray?aBBands[0]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aBBands[0],ty) );
            d2 = ( isIntray?aBBands[1]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aBBands[1],ty) );
            //indicatorsData.BBands.colorIndex = colorIndex;
			var preL = "BBands("+indicatorsData.BBands.parameter[0]+","+indicatorsData.BBands.parameter[1]+")"
			DataList.push({data:d1,startPos:null,colorIndex:colorIndex,color:tickerColors[colorIndex],pre:preL,legend:"U",name:"BBands",sq:0});
			DataList.push({data:d2,startPos:null,colorIndex:colorIndex,color:tickerColors[colorIndex],pre:preL,legend:"L",name:"BBands",sq:1});
            colorIndex++;
        }
        if (indicatorVisiable.PSAR) {
            var aPSAR = indicatorCal.calculatePSAR(aData,ty,indicatorsData.PSAR.parameter[0],indicatorsData.PSAR.parameter[1]);
            var d3;
			d3 = (isIntray?null:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aPSAR,ty) );
            //indicatorsData.PSAR.colorIndex = colorIndex;
			var l = "PSAR("+indicatorsData.PSAR.parameter[0]+","+indicatorsData.PSAR.parameter[1]+")"; 
			DataList.push({data:d3,startPos:null,colorIndex:colorIndex,color:tickerColors[colorIndex],legend:l,name:"PSAR"});
            colorIndex++;
        }
        if (indicatorVisiable.MACD) { 
            var aMACD  = indicatorCal.calculateMACD(aData,ty,indicatorsData.MACD.parameter[0],indicatorsData.MACD.parameter[1],indicatorsData.MACD.parameter[2]);
            indicatorsData.MACD.data = [];
            indicatorsData.MACD.data[0] = ( isIntray?aMACD[0]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aMACD[0],ty) );
            indicatorsData.MACD.data[1] = ( isIntray?aMACD[1]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aMACD[1],ty) );
            indicatorsData.MACD.data[2] = ( isIntray?aMACD[2]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aMACD[2],ty) );
        }
        if (indicatorVisiable.RSI) {
            var aRSI = indicatorCal.calculateRSI(aData,ty,indicatorsData.RSI.parameter[0]);
            indicatorsData.RSI.data = (isIntray?[aRSI]:[indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aRSI,ty)] );
        }
        if (indicatorVisiable.ROC) { 
            var aROC = indicatorCal.calculateROC(aData,ty,indicatorsData.ROC.parameter[0]);
            indicatorsData.ROC.data = (isIntray?[aROC]:[indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aROC,ty)] );
        }
        if (indicatorVisiable.WillR) { 
            var aWillR = indicatorCal.calculateWillR(aData,ty,indicatorsData.WillR.parameter[0]);
            indicatorsData.WillR.data = (isIntray?aWillR:[indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aWillR,ty)] );
        }
        if (indicatorVisiable.SStochastic) { 
            var aSStochastic  = indicatorCal.calculateSlowStochastic(aData,ty,indicatorsData.SStochastic.parameter[0],indicatorsData.SStochastic.parameter[1]);
            if (aSStochastic != null && aSStochastic.length >= 2) { 
                indicatorsData.SStochastic.data = [];
                indicatorsData.SStochastic.data[0] = ( isIntray?aSStochastic[0]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aSStochastic[0],ty) );
                indicatorsData.SStochastic.data[1] = ( isIntray?aSStochastic[1]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aSStochastic[1],ty) );
            }
        }
        if (indicatorVisiable.FStochastic) { 
            var aFStochastic  = indicatorCal.calculateFastStochastic(aData,ty,indicatorsData.FStochastic.parameter[0],indicatorsData.FStochastic.parameter[1]);
            if (aFStochastic != null && aFStochastic.length >= 2) { 
                indicatorsData.FStochastic.data = []; 
                indicatorsData.FStochastic.data[0] = ( isIntray?aFStochastic[0]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aFStochastic[0],ty) );
                indicatorsData.FStochastic.data[1] = ( isIntray?aFStochastic[1]:indicatorCal.filtrateCalculatedData(stIndx,endInx,true,aFStochastic[1],ty) );
            }
        }
    }
    
    function convertDividendData(data,priceData) { 
        DList = [];
        DividendInfoList = [];
        if (data == null || data.length < 1) {
            return;
        }
        if (priceData == null || priceData.length<1) {
            return;
        }
        var k = 0;
        for(var i = 0; i < data.length; i++) {  
            var dp = []; 
            var d = data[i].DateIndex;
            var ls = priceData[0][priceData[0].length-1];
            for ( j = k; j < priceData.length; j++) {   
                if (j > 0) {
                    ls = priceData[j-1][priceData[j-1].length-1];
                }
                if (d <= priceData[j][priceData[j].length-1] && d >= ls) {
                    dp.push(priceData[j][0]);
                    k=j;
                    compairTickers.length > 0 ?dp.push(priceData[j][1]):dp.push(priceData[j][2]);
                    break;
                }
            }
            if (dp.length > 0) {
               dp.push(data[i].Type); 
               dp.push(d);
               DividendInfoList.push(data[i].Desc);
               DList.push(dp);
            }
        }
    }
    
    function calculateReturn() { 
        var sIndx = 0;
        if (chartStyle != "lines") {
            sIndx = 1;
        }
        for (var i = sIndx; i < DataList.length; i++) {
            calculateReturnValue(DataList[i],null);
        }
        /*if (chartStyle == "lines") {
			var sma = indicatorsData.SMA
			indicatorsData.SMA.startPos = [null,null,null];
            for (var j = 0;j < sma["num"].length; j++) {
                if (sma["num"][j] != null) {
                    calculateReturnValue(indicatorsData.SMA,j);
                }    
            }
			var ema = indicatorsData.EMA;
			indicatorsData.EMA.startPos = [null,null,null];
            for (var j = 0;j < ema["num"].length; j++) {
                if (ema["num"][j] != null) {
                    calculateReturnValue(indicatorsData.EMA,j);
                }    
            } 
            if (indicatorVisiable.BBands) {
                var aBBands = indicatorsData.BBands;
                aBBands.startPos = [];
                calculateReturnValue(aBBands,0);
                calculateReturnValue(aBBands,1);
            }
            if (indicatorVisiable.PSAR) {
                var aPsar  = indicatorsData.PSAR;
                aPsar.startPos = null;
                calculateReturnValue(aPsar,null);
            }
        }*/
    }
    
    function calculateReturnValue(oList,indx) {  
        var list = oList.data, pos = oList.startPos;
        if (indx != null) { 
            list = oList.data[indx]; pos = oList.startPos[indx];
        }
        var sp = (pos==null||isNaN(pos)?0:pos);
        if (list == null || list.length < 1) {
           return;
        }
        for (var i = 0; i < list.length; i++) { 
            if (list[i] == null || list[i].length < 2 || isNaN(list[i][1])) {
                continue;
            }
            if(sp == 0) {
                sp = list[i][1];
				if(indx != null){
				 	oList.startPos[indx] = sp;
				} else {
					oList.startPos = sp;
				}
            }  
            list[i][1] = 100*(list[i][1]-sp)/sp;
        }
    }
    
    function getMiniTickSize() {
        if (miniChartFrequency == "d") return 54;
        else if (miniChartFrequency == "w") return 10;
        else if (miniChartFrequency == "m") return 10;
        else return 10;
    }
    
    function getChartData(data,st,ya,cor,w) {
		var isfi = false, fillC = null;
        if(st == "lines") return {data:data,lines:{show:true},yaxis:ya,color:cor,lineW:w};
		else if(st == "arealine")return {data:data,lines:{show:true,fill:true,fillColor:"#c4ccd7",a:0.5},yaxis:ya,color:cor,lineW:w};
        else if (st == "points") return{data:data,points:{show:true},yaxis:ya,color:cor};
        else if (st =="bars") return {data:data,bars:{show:true},yaxis:ya,color:cor};
        else if (st =="candle"&&timeRangeType != "oneday"&& timeRangeType != "fivedays") return {data:data,candleStick:{show:true},yaxis:ya};
        else if (st =="ohlc" && timeRangeType != "oneday"&& timeRangeType != "fivedays") return {data:data,OLHC:{show:true},yaxis:ya,color:cor};
        else if (st =="img" && timeRangeType != "oneday"&& timeRangeType != "fivedays") return {data:data,imagePoints:{show:true},yaxis:ya};
        else return {data:data,lines:{show:true},yaxis:ya,lineW:w};
    }
    
    function tickFormat(v) {
        if (DataList != null && DataList[0].data != null && DataList[0].data[0] != null ) {
            return (v<DataList[0].data.length&&DataList[0].data[v] != null)?formateLabelDate(DataList[0].data[v][DataList[0].data[v].length-1]):"";
        }    
    }
    
    function getChartOption(type)  { // get the config for chart.
        if (type == "priceLine") {
           return { xaxis: { mode:null,tickSize:ticksize,showLabel:true,labelWidth:60,labelHeight:12,ticks:Ticks,
                       tickFormatter:tickFormat},
                       yaxis: {labelWidth:50,labelHeight:12},          
                       grid: { hoverable: true, clickable: true},
					   colors:tickerColors,
					   selection:{mode:"x"}
                    };
        } else if (type == "indicatorLines") {
           return { xaxis: { mode:null,tickSize:ticksize,showLabel:false,ticks:Ticks,
                      tickFormatter:null},
                      lines: {lineWidth:1},
                      yaxis: {labelWidth:50,labelHeight:12},
					  colors:tickerColors        
                    };    
        } else if (type == "volumeBar") {
           return { xaxis: { mode:null,tickSize:ticksize,showLabel:false,ticks:Ticks,
                      tickFormatter:null},       
                      yaxis: {labelWidth:50,labelHeight:12,min:0},        
                      bars:{show:false,barWidth:1.5,lineWidth:0,fillColor:"#2f4880"},
					  colors:tickerColors
                    };
        }  else if (type == "miniChart") {
            return { xaxis: { mode:null,tickSize:getMiniTickSize(),showLabel:true, tickFormatter:function(v){if(MiniChartData!=null&&MiniChartData[0]!=null&&MiniChartData[0].length>0)return (v<MiniChartData[0].length&&MiniChartData[0][v]!=null)?formateMiniLabelDate(MiniChartData[0][v][MiniChartData[0][v].length-1]):"";},labelWidth:50,labelHeight:8,posStyle:"inner"},       
                       yaxis: {labelWidth:0,labelHeight:12,showLabel:0,autoscaleMargin: 0.02,ticks:[]},        
                       lines:{fill:true,showHelp:true,fillColor:"#d9dfeb",a:1},
					   colors:tickerColors
                   };
        }            
    }
       
    this.changeOutSize  = function(wd,hg) {  
        outHeight = hg;
        var wid =  wd < minW?minW:wd;
        setSize(wid,hg);
        if(p != null) {
            p.changeOutSize(pChartWidth,pChartHeight);
        }
        if(v != null && showVolume) {
            v.changeOutSize(vChartWidth,vChartHeight);
        }
        if(m != null && UIConfig.Chart && (UIConfig.Chart.showTimeSlider || UIConfig.CustomUI)) {
            m.changeOutSize(pChartWidth,miniChartHeight);
            if(sliderObj != null ) {
                sliderObj.changeOutSize(pChartWidth,miniChartHeight);
            }
        }
        for(var o in indicatorVisiable) {
            if(indicatorVisiable.hasOwnProperty(o) && indicatorVisiable[o]) {
                if(typeof(indicatorFlots[o]) != "undefined" && indicatorFlots[o]) {
                    indicatorFlots[o].changeOutSize(pChartWidth,60);
                }
            }
        }
		for(var o in valuations){
			if(valuations[o].oFlot != null){
				valuations[o].oFlot.changeOutSize(pChartWidth,60);
			}
		}
    };
    
    function setSize(wd,hg) { 
        miniChartWidth = wd;
        pChartWidth = wd;
        vChartWidth = wd;
        var ih = getIndicatorChartHeight();
		var valuationChartH = getValuationChartHeight();
        var h = hg-ih-valuationChartH-35-36-5;
        if (UIConfig.chartCtrl == "default") {
             if (UIConfig.pCtrl1 != null ) h -= 28;
             if (UIConfig.pCtrl2 != null ) h -= 28;
             if (UIConfig.vCtrl != null) h-= 28;
        } else if (UIConfig.chartCtrl == "rtqPanel") {
             h-=40;
        }
        var hh = h>160?h:160;
        var ph= (hh)*0.8;
        var vh = (hh)*0.2;
		if(!showVolume){
			vh = 0;
			ph = hh;
		}
        pChartHeight = ph;
        vChartHeight = vh;
        outObj.css({width:wd+"px",height:hg+"px"});
        priceChartContainer.css({width:wd+"px", height:ph+"px", "float":"left"});
        volChartContainer.css({width:wd+"px", height:vh+"px", "float":"left"});
        miniChartContainer.css({width:wd+"px", height:"35px", "float":"left"});
        outObj.find(".rtc-chart-container").find(".rtc-chart").css({width:wd+"px"});
    }
    
    function calculateFrequency(startDate) { 
       var f = "d";
       if (startDate < dailyStartDate) f = "w";
       if (startDate < weeklyStartDate) f = "m"; 
       return f;
    }
    this.setCursorType = function(iTy) {
        chartCursor = iTy;
    }
    this.setChartType = function(ty){
        chartTypeSelected("",ty);
    }
    this.setFrequency = function(f) {
        frequencySelected("",f);
    }
    this.switchVolumeChart = function(bshow) {
        if(showVolume != (bshow == 1)) {
            showVolume = (bshow != 1);
            toggleVolumeChartVision();
			if(typeof(chartObj.volumeChartChanged) == "function" ){
				chartObj.volumeChartChanged({showVolumeChart:showVolume});
			}
        }
    }
	this.switchFairValue = function(bshow){
		var b = (bshow == 1)
		if(isFairValue != b){
			isFairValue = bshow;
			if(zoomRange > 2) Draw(false);
		}	
	}
    this.switchDividend = function(bshow) {
        var b = (bshow == 1);
        if(isDividend != b){
            isDividend = b; 
            if(zoomRange > 2) Draw(false);
			if(typeof(chartObj.dividendChartChanged) == "function" ){
				chartObj.dividendChartChanged({showDividendChart:isDividend});
			}
        }
    }
    this.switchSplits = function(bshow) {
        var b = (bshow == 1);
        if(isSplits != b){
            isSplits = b; 
            if(zoomRange > 2) Draw(false);
			if(typeof(chartObj.splitsChartChanged) == "function" ){
				chartObj.splitsChartChanged({showSpiltsChart:isSplits});
			}
        }
    }
    this.changeRange = changeRange; // change the date range
    this.changeDatePeriod = function (s,e) {   // customize date range. date format: (yyyy-MM-dd)
        startDate = convertToDate(s);
        endDate = convertToDate(e);
        timeRangeType = "custom";
        changeRange(11); 
		if(typeof(chartObj.datePeriodChanged) == "function" ){
			chartObj.datePeriodChanged({timeRangeType:timeRangeType,zoomRange:zoomRange,startDate:s,endDate:e});
		}
    }
    this.addCompairTickers = function(symbols) {         // add compair tickers.
    	validateCompairTicker(symbols);
        //checkTickerInfo(symbols,objName+".SecruritySelected");
    }
    this.removeCompairTicker = function(ticker) {           // remove a compair ticker.
        if(compairTickers == null || compairTickers.length < 1) {
            return;
        }
		var t = ticker;
		if(ticker.split(":").length > 1){   // exch:ticker
			t = getPidByExchAndTicker(ticker);    // convert to performanceId.
		}
        for(var i = 0; i < compairTickers.length; i++) {
            if(t == compairTickers[i]){
				if(typeof(chartObj.afterRemoveTickers)== "function"){  // after callback;
					chartObj.afterRemoveTickers({symbol:tickerInfos[t]});
				}
				this.removeTicker(i);
                break;
            }
        }		
    }
    
    this.addIndicators = function (name) {     // add or update technical indicator to chart.
        var n = name.toLocaleUpperCase();
        if(n == "SMA") {
            addSMV(arguments[1],arguments[2]);  
        } else if(n == "SMAV") {
            addSMVV(arguments[1]);
        } else if(name == "EMA") {
            setIndicatorParas(name,arguments[1],arguments[2],arguments[3]);   // three lines. if draw one line set other prams to null.
            indicatorVisiable[name] = true; 
            Draw(false);  
        } else if(name == "BBands") {
            setIndicatorParas(name,arguments[1],arguments[2]);
            indicatorVisiable[name] = true; 
            Draw(false);
        } else if(name == "PSAR") {
            setIndicatorParas(name,arguments[1],arguments[2]);
            indicatorVisiable[name] = true; 
            Draw(false);
        } else if(indicatorFlots.hasOwnProperty(name)) {  // add new indicator chart.
            if(name == "MACD") {
                setIndicatorParas(name,arguments[1],arguments[2],arguments[3]);
            } else if(name == "RSI") {
                setIndicatorParas(name,arguments[1]);
            } else if(name == "ROC") {
                setIndicatorParas(name,arguments[1]);
            } else if(name == "SStochastic") {
                setIndicatorParas(name,arguments[1],arguments[2]);
            } else if(name == "FStochastic") {
                setIndicatorParas(name,arguments[1],arguments[2]);
            } else if(name == "WillR") {
                setIndicatorParas(name,arguments[1]);
            } 
            
            if(UIConfig.autoHeight && !indicatorVisiable[name]) {
                outHeight += 78;
            }
			indicatorVisiable[name] = true;
            setSize(pChartWidth,outHeight);
            if(p != null){ 
                p.resizeCanvas(pChartWidth,pChartHeight);
            }
            if(v != null && showVolume) {
                v.resizeCanvas(pChartWidth,vChartHeight);
            }
            Draw(true); // only filter indicator data
            if (UIConfig.Chart && (UIConfig.Chart.showTimeSlider || UIConfig.CustomUI)) {
                drawMiniChart(); 
                //setTimeout(function(){chartObj.resetSlider();},100);
            }
            buildIndicatorLegend(-1);
        }
        pChartReady = true;
		if(typeof(chartObj.afterAddIndicators)== "function"){  // after callback;
			chartObj.afterAddIndicators({indicatorName:n,paras:arguments});
		}
    }
    this.removeIndicators = function (name) {
		if(name == "SMA") {
            this.removeSMV(arguments[1]);
        } else if(name =="SMAV") {
            this.removeSMVV();
        }else if(indicatorsData[name]) {     // remove a new indicator chart.
            if(name == "EMA") {
                if(arguments.length > 1) {
                    this.removeOverlayIndicator(name+"_"+arguments[1]);  // remove one of ema lines.
                } else {
                    this.removeOverlayIndicator(name); //remove all EMA lines. 
                }
            } else if(name == "PSAR" || name == "BBands") {
                this.removeOverlayIndicator(name);
            } else {
                this.removeIndicator(name);  
            }               
        } else if(indicatorVisiable[name]) {
            this.removeOverlayIndicator(name);        
        } 
        if(typeof(chartObj.afterRemoveIndicator)== "function"){  // after callback;
			chartObj.afterRemoveIndicator({indicatorName:name,paras:arguments});
		}
    }
    function changeRange(num) {
        writeTimeLog("change range num",null,null);
        zoomRange = num;
        if (num != 11) {
           endDate = latestTradeDate;
        }
        if(num == 1) {
            qt=16;timeRangeType = "oneday"; startDate= endDate;
        } else if (num == 2) {
            qt = 32;timeRangeType = "fivedays"; startDate = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()-5);
        } else if (num == 3) {
            timeRangeType = 'onemonth'; flag = true; startDate= new Date(endDate.getFullYear(), endDate.getMonth()-1, endDate.getDate()); 
        } else if (num == 4) {
            timeRangeType = 'threemonths'; flag = true; startDate = new Date(endDate.getFullYear(), endDate.getMonth()-3, endDate.getDate());
        } else if (num == 5) { 
            timeRangeType = 'ytd'; flag = true; startDate = new Date(endDate.getFullYear(), 0, 1); 
        } else if (num == 6) {
            timeRangeType = 'year'; flag = true; startDate= new Date(endDate.getFullYear() - 1, endDate.getMonth(), endDate.getDate());
        } else if (num == 7) {
            timeRangeType = 'threeyears'; flag = true; startDate = new Date(endDate.getFullYear() - 3, endDate.getMonth(), endDate.getDate());
        } else if (num == 8) {
            timeRangeType = 'fiveyears'; flag = true; startDate = new Date(endDate.getFullYear() - 5, endDate.getMonth(), endDate.getDate());
        } else if (num == 9) {
            timeRangeType = 'tenyears'; flag = true; startDate = new Date(endDate.getFullYear() - 10, endDate.getMonth(), endDate.getDate());
        } else if (num == 10) {
            timeRangeType = 'max'; flag = true; startDate = new Date(1950, 0, 1); 
        } else if (num == 11) {
            timeRangeType = "custom"; 
            if(ddlZoom != null) ddlZoom.setTextValue("Custom","11");
        }
        changeFrequrystyle(num);
        Draw(true);
		if(typeof(chartObj.rangeChanged)== "function"){  // after callback;
			chartObj.rangeChanged({rangeType:timeRangeType,zoomRange:zoomRange,startDate:formatDate(startDate),endDate:formatDate(endDate)});
		}
    }
    
    function changeFrequrystyle(num) {
        var f,mf ;
        if (num == 1 || num == 2) {
            f = "minute"; mf="d"; frequency=f;
        } else { 
            f = calculateFrequency(startDate); mf = f;
        }
        changeFrequency(f);
        if ((num == 1|| num == 2) && chartStyle != "lines") {
            chartStyle="lines"; 
            var obj =chartStyleULElm.find("a[value='lines']"); 
            chartStyleElm.html(obj.html());
            chartStyleULElm.find("a").removeClass("on");
            obj.addClass("on");
        } else {
            if (rightMenuElm != null && rightMenuElm.length > 0) {
                rightMenuElm.find("UL[idkey='timeRange']").find("a").removeClass("on");
                rightMenuElm.find("UL[idkey='timeRange']").find("a[value='"+num+"']").addClass("on");
            }
        }
        if (mf != miniChartFrequency) {
            miniChartFrequency = mf;setTimeout(function(){DrawMiniChart();},500);
        }
        setTimeout(function(){chartObj.setSliderHandleValues();},1100);
    }
    
    function convertToNumber(fre) {
        if (fre=="minute") return 0;
        else if(fre=="d") return 1;
        else if(fre=="w") return 2;
        else if(fre =="m") return 3;
        else return 1;
    }
    
    function changeFrequency(f) {
        if (convertToNumber(f) > convertToNumber(frequency)) {
            frequency = f;
        }
        if (UIConfig.pCtrl1 == null || !UIConfig.pCtrl1.showFrequency) {
            frequency = f; 
        }// if can't exist frequency control element
        bindFrequencyMenu(f);
    }
    
    function bindFrequencyMenu(f) {
        if (UIConfig.pCtrl1 == null || !UIConfig.pCtrl1.showFrequency) { 
            return;
        }
        var st = convertToNumber(f);
        var stt = convertToNumber(frequency);
        var str=""; var sValue = frequencyListData[0].val;
        var sName = frequencyListData[0].txt;
        var $r = rightMenuElm.find("UL[idkey='freul']");
        var $rMenu;
        if (rightMenuElm != null && rightMenuElm.length > 0) { 
            $r.find("a").removeClass("on"); $r.find("Li").hide();
        }
        if(st == 0) {
            ddlFrequency.showItemByValue("minute");
            ddlFrequency.hideItemByValue("d");
            ddlFrequency.hideItemByValue("w");
            ddlFrequency.hideItemByValue("m");
            if (rightMenuElm != null && rightMenuElm.length > 0) {
                $r.find("A[value='"+sValue+"']").parent().show();
                $r.find("A[value='"+sValue+"']").addClass("on");
            } 
        } else {
            for (var i = st; i < frequencyListData.length; i++) {
                sName = frequencyListData[i].name; 
                sValue = frequencyListData[i].val; 
                $rMenu = $r.find("A[value='"+sValue+"']");
                if (i == stt) { 
                    $rMenu.addClass("on");
                }
                $rMenu.parent().show();
                if(ddlFrequency != null) {
                    ddlFrequency.showItemByValue(sValue);  
                }
            }
            for(var j = 0;j < st; j++) {
                if(ddlFrequency != null) {
                    ddlFrequency.hideItemByValue(frequencyListData[j].val);  
                }
            }
        } 
        if(ddlFrequency != null) {
            ddlFrequency.setTextValue(frequencyListData[stt].txt,frequencyListData[stt].val);
        }
    }
      
    function getTradeDateTime(t) {
        if (timeRangeType == "oneday" || timeRangeType == "fivedays") { //intrade day date.
            return new Date(t);
        } else {
            return new Date(dateIndexStart + t * dateIndexBase); 
        }    // historical date 
    }
    
    function formateLabelDate(tick) {      
        var t =  parseInt(tick,10);
        if (t < 1440) t = trimDate(new Date()).getTime()+t*60*1000;
        var dt = getTradeDateTime(t);
        if (dt != null && (timeRangeType == "oneday" || timeRangeType == "fivedays")) {
            return (timeRangeType=="oneday")?DTFormat.ToShortTime(dt):DTFormat.ToLongDate(dt);
        } else { 
            return DTFormat.ToLongDate(dt);
        }
    }
    
    function formateMiniLabelDate(tick) {
        var t =  parseInt(tick,10);
        var dt =  toDateFromIndex(t);
        if( dt == null) {
            return "";
        }
        if (miniChartFrequency == "d") {
            return dt.getFullYear(); //DTFormat.ToShortYearMonth(dt);
        } else {
            return dt.getFullYear().toString().substr(2,2);
        }
    }
    
    function formateLegdendDate(tick) {  
        var t =  parseInt(tick,10);
        if (t < 1440) {
            t = trimDate(new Date()).getTime()+t*60*1000;
        }
        var dt = getTradeDateTime(t);
        if (timeRangeType == "oneday" || timeRangeType == "fivedays") {
            return (dt==null)?"": (DTFormat.ToLongDate(dt) +" "+DTFormat.ToShortTime(dt));
        } else { 
            return (dt==null)?" " : DTFormat.ToLongDate(dt);
        }
    }
    
    function setTimeRangeBox(s,e) { 
        if (s == null || e == null) {
            return;
        }
        startBoxElm.attr("value",DTFormat.ToLongDate(s));
        endBoxElm.attr("value",DTFormat.ToLongDate(e));
    }
    
    function getVolInfo(index) {
        var vol = (VolList == null || index > VolList.length || VolList[index] == null)?null: VolList[index][1];
        return NumFmt.Fmt(vol * 1000000, true);
    }
    
    function getPriceValue(p,isRt,spos) { 
        var pp= (isRt)?(spos*(100+p)/100):p;
        return !pp||isNaN(pp)?"--":pp.toFixed(2);
    }
    
    function getReturnValue(p,isRt,spos) { 
        var clo = "", str = "";
        if (isNaN(spos)) {
            str = "--";
        } else {  
            var dx = isRt?(spos*p/100):(p-spos); clo = (dx>0)?"green":"red";
            var pp = isRt?p:100*dx/spos;
            str =  isNaN(pp)?"--":dx.toFixed(2)+"|"+pp.toFixed(2)+"%";
        }
        return "<span style='color:"+clo+"'>"+str+"</span>";
    }
    function legendOnClick(elm){   // legend onclick handler.
		var name = elm.attr("name");
		var seq = elm.attr("seq");
		var sq = elm.attr("sq");
		var num = elm.attr("num");
		elm.remove();
		if(name=="SMA"){
			chartObj.removeSMV(parseInt(sq,10));
		} else if(name=="EMA"){
			chartObj.removeOverlayIndicator(name+"_"+num);
		} else if(name =="BBands" || name == "PSAR"){
			chartObj.removeOverlayIndicator(name);
		} else if(name == "FairValue"){
			chartObj.switchFairValue(1);
		} else if(name == "SMAV"){
			chartObj.removeSMVV();
		} else {
			chartObj.removeTicker(parseInt(seq,10)-1);
		}
	}
    function mouseOutChart() {
        var isDefault = UIConfig.chartCtrl =="default"?true:false;
        if (chartCursor == 1) {
            crosshairLegendElmContainer.hide(); 
            lineXElm.hide(); lineYElm.hide();
        }
		$(".addTrendLineHint").hide(); //jacky
		buildLegend(null,null,true,isDefault); 
    }
    
    function createLegendSpan(sq,name,value,cIndex,extP) {
        var color = tickerColors[cIndex]; var v = value ==""?"":":"+value;
		var str;
		if (sq == 0) {
			str = '<span class = "rtc-lg-icn' + cIndex + '" style="color:' + color + '">' + name + v + '</span>&nbsp;&nbsp;&nbsp;';
		}
		else {
			str = '<span class="rtc-legend"  ' + extP + '><span class = "rtc-lg-icn' + cIndex + '" style="color:' + color + '">' + name + v + '</span></span>&nbsp;&nbsp;&nbsp;';
		}
		return str;
    }
    
    function createLegendCrosshairRow(sq,name,value,isTop,cIndex) {
        var str = ""; var cls = isTop?"rtc-legend-row-top":"rtc-legend-row";
        var claName = "rtc-lg-icn"+cIndex;
        if (sq >= 2) { 
            str+='<div class="rtc-legend-row" ><strong class="'+claName+'">'+name+'</strong><span>'+value+'</span> </div>';
        } else if(sq == 0){
            str+= '<div class="'+cls+'"><strong class="'+claName+'">'+name+'</strong></div><div  class="rtc-legend-table"><table cellpadding="0" cellspacing="0">';
            str+='<tr><td>Open: '+value[0]+'</td><td>High: '+value[1]+'</td></tr><tr><td>Close: '+value[2]+'</td><td>Low: '+value[3]+'</td></tr><tr><td style="border-bottom:0" colspan="2">Volume: '+value[4]+'</td></tr></table></div>';                
        } else if (sq == 1) {
             str+= str+= '<div class="'+cls+'"><strong class="'+claName+'">'+name+'</strong></div><div  class="rtc-legend-table"><table cellpadding="0" cellspacing="0">';
            str+='<tr><td>Price: '+value[0]+'</td><td>Volume: '+value[1]+'</td></tr></table></div>';                
        }            
        return str;        
    }
       
    function buildLegend(item,pos,isMouseout,isDefaut) {
        if ((chartCursor == 3 || !pChartReady) && !isMouseout) return false;    // chart has not ready ;
        var str="", time = "", vol,volStr="", dataIndex = (isMouseout||item==null||item.dataIndex==null)?-1:item.dataIndex; 
        if (chartCursor == 0 || isMouseout) {
            if (dataIndex > 0) {
                time = (item==null)?"--":formateLegdendDate(item.datapoint[item.datapoint.length-1]);
				str = "Time:"+time+"&nbsp;&nbsp;"; 
            } else {
                str= DTFormat.ToLongDate(startDate)+"-"+DTFormat.ToLongDate(endDate)+"&nbsp;&nbsp;"; 
				setTimeRangeBox(startDate,endDate);
            }    
			vol = (!VolList || dataIndex > VolList.length || VolList[dataIndex] == null)?null: VolList[dataIndex][1];
            volStr =" volume:"+getVolInfo(dataIndex);  //get volume infomation
        } else {
            time = (dataIndex < 0 || item.datapoint == null)?"":formateLegdendDate(item.datapoint[item.datapoint.length-1]);
        }
		// customize callback.
		if(!isMouseout&&typeof(chartObj.chartMouseoverCallback)== "function"&& chartCursor == 0 ){//mouse over and chartCursor == 0
			chartObj.chartMouseoverCallback(dataIndex,DataList,vol,indicatorsData.SMAV,startDate,endDate);
			if(!isMouseout && item != null && timeRangeType != "oneday" && timeRangeType != "fivedays" && (isDividend || isSplits)) {
	            showDividendInfo(item);
	        }
	        if(!isMouseout && item != null && item.dataIndex != null) {
	            buildIndicatorLegend(item.dataIndex);
	        }
			return ;
		} else if(isMouseout&&typeof(chartObj.chartMouseoutCallback)== "function"){ //mouse out
			chartObj.chartMouseoutCallback(-1,DataList,vol,indicatorsData.SMAV,startDate,endDate);
			return;
		}
		
		if(DataList && DataList.length && DataList.length > 0){   // price chart data.
			var bIsReturn = (compairTickers.length > 0)?true:false;
			var lg = DataList.length,d, val,sq, temp,leg,expara;
			for(var i = 0;i < lg;i++){	
				leg = DataList[i].legend;
				if (isMouseout) {
					d = DataList[i].data && DataList[i].data.length ? DataList[i].data[DataList[i].data.length-1]:null;
					sq = i;
					if(DataList[i].name == "BBands"&& DataList[i].sq == 0){
						continue;
					}
					if (DataList[i].name == "BBands") {
						leg = DataList[i].pre;
					}
					if(isDefaut){
						val = d==null||!d.length?"--":getReturnValue(d[1],bIsReturn,DataList[i].startPos);
					}else{
						val = "";
					}
					expara="name="+DataList[i].name+" "+"seq="+i;
					if(typeof(DataList[i].sq) != "undefined"){
						expara+=" sq="+DataList[i].sq;
					}
					if(typeof(DataList[i].num) != "undefined"){
						expara+=" num="+DataList[i].num;
					}
				}
				else {
					d = DataList[i].data && DataList[i].data.length && dataIndex < DataList[i].data.length ? DataList[i].data[dataIndex]:null;
					if (DataList[i].name == "BBands" && DataList[i].sq == 0) {
						temp = d == null ? "--" : getPriceValue(d[1], bIsReturn, DataList[i].startPos);
						continue;
					}
					if (i == 0) { // main ticker.
						sq = 0;
						var isMReturn = (compairTickers.length > 0 && chartStyle == "lines") ? true : false;
						if (zoomRange > 2) {
							if (chartCursor == 0) {
								val = d == null || d.length < 5 ? "--" : ("open:" + d[4].toFixed(2) + " high:" + d[2].toFixed(2) + " low:" + d[3].toFixed(2) + " close:" + (getPriceValue(d[1], isMReturn, DataList[i].startPos)));
								val += volStr;
							}
							else {
								val = d == null || d.length < 5 ? ["--", "--", "--", "--", getVolInfo(dataIndex)] : [d[4].toFixed(2), d[2].toFixed(2), getPriceValue(d[1], isMReturn, DataList[i].startPos), d[3].toFixed(2), getVolInfo(dataIndex)];
							}
						}
						else {
							if (chartCursor == 0) {
								val = (d == null ? "--" : getPriceValue(d[1], isMReturn, DataList[0].startPos)) + volStr;
							}
							else {
								sq = 1; // 
								val = [(d == null ? "--" : getPriceValue(d[1], isMReturn, DataList[0].startPos)), getVolInfo(dataIndex)];
							}
						}
					}
					else {
						sq = 2;
						val = d == null ? "--" : getPriceValue(d[1], bIsReturn, DataList[i].startPos);
						if (DataList[i].name == "BBands") {
							val = "U:" + temp + " L:" + val;
							leg = DataList[i].pre;
						}
					}
				}
				if (chartCursor == 0 || isMouseout) { 
		            str+= createLegendSpan(sq,leg,val,DataList[i].colorIndex,expara);
		        } else {
		            str+= createLegendCrosshairRow(sq,leg,val,(i==1?true:false),DataList[i].colorIndex);
		        }
			}
		}
		var smav = indicatorsData.SMAV;
        if (smav && smav["num"] != null && smav["data"] != null) {
			var lege = "SMAV("+smav["num"]+")";
            if(!isMouseout && item != null) {
                val = (dataIndex>=smav["data"].length||smav["data"][dataIndex]==null?"--":NumFmt.Fmt(smav["data"][item.dataIndex][1] * 1000000, true));
            }else{
				val = "";
			}
            if(chartCursor == 0 || isMouseout) {
				var exp = "name=SMAV";
                str += createLegendSpan(2,lege,val,1,exp);
            } else { 
                str += createLegendCrosshairRow(2,lege,val,false,1);
            }
        }
        if (chartCursor == 0||isMouseout) {
            legendElm.html(str);
        } else { 
            crosshairLegendElm.html(str);
            var left = pos.pageX-chartOffset.left+5; var top = pos.pageY - chartOffset.top+5; 
            //var w = crosshairLegendElmContainer.width();
            lineXElm.css({top:top-2});
            lineYElm.css({left:left-2});
            yLabelElm.html(time);
            if (item != null) {
               xLabelElm.html(item.y.toFixed(2));
            }
            lineXElm.show();lineYElm.show();
            if (left+220 > pChartWidth) {
                left = left - 220;
            }
            crosshairLegendElmContainer.css({left:left,top:top}); crosshairLegendElmContainer.show();
        }
        str = "";
        if(!isMouseout && item != null && timeRangeType != "oneday" && timeRangeType != "fivedays" && (isDividend || isSplits)) {
            showDividendInfo(item);
        }
        if(!isMouseout && item != null && item.dataIndex != null) {
            buildIndicatorLegend(item.dataIndex);
			buildValuationLegend(item.dataIndex);
        }
    }
     
    this.removeOverlayIndicator = function(sKey) { 
        var aKey = sKey.split("_");
        var sName = aKey[0], hasEMA = false;
        if (aKey.length < 2) {
            chartObj.removeIndicator(sKey);
        } else {                         //EMA
            var ind = parseInt(aKey[1]);
            for (var i = 0; i < indicatorsData[sName]["num"].length; i++) { 
                if (indicatorsData[sName].num[i] == ind) {
                    indicatorsData[sName].data[i] = null;
                    indicatorsData[sName].num[i] = null;
					break;
                }
                if (indicatorsData[sName].num[i] != null){
                    hasEMA = true;
                } 
            }
        }
		for(var j = 0; j < DataList.length; j++){
			if(DataList[j].name == sName){
				if(hasEMA && i == DataList[i].sq){
					DataList.splice(j,1);
				}else{
					DataList.splice(j,1);
				}
			}
		}
        if (!hasEMA) {
            indicatorVisiable[sName] = false;
            if (UIConfig.chartCtrl == "default") {
                $chkbox = outObj.find("li[idkey='indicatorList']").find("a[value='"+sName+"']").addClass("rtq-chkbox_off");
            } else {
                $chkbox = $settingPanel.find("li[idkey='indicatorList']").find("a[value='"+sName+"']").addClass("rtq-chkbox_off");
            }
        }
        Draw(true);
    }
    
    this.removeIndicator = function(sName) {
        if (indicatorFlots[sName] != null) { // hide the new indicator chart
           outObj.find("[idkey='"+sName+"Legend']").hide();
           outObj.find("[idkey='"+sName+"Chart']").hide();
           if(UIConfig.autoHeight) {
               outHeight -= 78;    //
           }
        }
        if (UIConfig.chartCtrl == "default") {
            $chkbox = outObj.find("li[idkey='indicatorList']").find("a[value='"+sName+"']");
        } else {
            $chkbox = $settingPanel.find("li[idkey='indicatorList']").find("a[value='"+sName+"']");
        }
        $chkbox.addClass("rtq-chkbox_off");
        indicatorVisiable[sName] = false;
        if(indicatorFlots[sName] != null) {    // remove a new chart.
            this.changeOutSize(pChartWidth,outHeight);        
        }
        if (sName != "EMA") {
            indicatorsData[sName].data = null;
            for (var i = 0; i < indicatorsData[sName].parameter.length; i++) { 
                indicatorsData[sName].parameter[i] = null;
            } // clear the data and parameters.
        } else { 
            for(var j = 0 ;j < indicatorsData[sName]["num"].length; j++) {
                indicatorsData[sName].data[j] = null;
				indicatorsData[sName].num[j] = null;
            }
        }
    };
    
    function formatIndicatorNum(oData,i,index) { 
        if(oData == null || oData.length <= i) return "--"; 
        else if(oData[i] == null||oData[i].length <= index || index < 0) return "--";
        else return (oData[i][index] == null || isNaN(oData[i][index][1])?"--":oData[i][index][1].toFixed(2) );
    }
    
    function buildSingleIndicatorLegend(sName,oD,oData,aField,index,ty) {
        var $ind = outObj.find("[idkey='"+sName+"Legend']"); 
		var env = ty=="v"?"removeValuationChart":"removeIndicators";
        var sStr = "<div style='margin-top:2px;' idkey="+sName+"innerL onclick="+objName+"."+env+"('"+sName+"')"+" onmouseover="+objName+".overIndicatorLegend('"+sName+"')  onmouseout="+objName+".outIndicatorLegend('"+sName+"')>";
        for (var i = 0; i < aField.length; i++) {
            sStr+='<span class="rtc-lg-icn'+i+'" style ="color:'+tickerColors[i]+'">'+aField[i]+formatIndicatorNum(oData,i,index)+'</span>&nbsp;&nbsp;';
        }
        sStr+="</div>";
        $ind.html(sStr);
    }
    function buildValuationLegend(index){
		var sName,oData,aField;
		for(var o in valuations){
			if(valuations[o].show){
				oData = [valuations[o].data];
				aField = valuations[o].name+": ";
				buildSingleIndicatorLegend(o,null,oData,[aField],index,"v");
			}
		}
	}
    function buildIndicatorLegend(index) {
         var sName, oD, oData, aField;
        if (indicatorVisiable.MACD) {
            sName = "MACD"; oD  = indicatorsData.MACD; oData = indicatorsData.MACD.data;
            aField = ["MACD("+oD.parameter[0]+","+oD.parameter[1]+"):","EMA("+oD.parameter[2]+"):","Divergence:"];
            buildSingleIndicatorLegend(sName,oD,oData,aField,index);
        }
        if (indicatorVisiable.RSI) { 
            sName = "RSI"; oD  = indicatorsData.RSI;
            oData = indicatorsData.RSI.data; aField = ["Relative Strength("+oD.parameter[0]+"):"];
            buildSingleIndicatorLegend(sName,oD,oData,aField,index);
        } 
        if (indicatorVisiable.ROC) { 
            sName = "ROC"; oD  = indicatorsData.ROC;
            oData = indicatorsData.ROC.data; aField = ["Rate of Change("+oD.parameter[0]+"):"];
            buildSingleIndicatorLegend(sName,oD,oData,aField,index);
        } 
        if (indicatorVisiable.WillR) {  
            sName = "WillR";  oD  = indicatorsData.WillR; 
            oData = indicatorsData.WillR.data; aField = ["Williams %R("+oD.parameter[0]+"):"];
            buildSingleIndicatorLegend(sName,oD,oData,aField,index);
        } 
        if (indicatorVisiable.SStochastic) {
           sName = "SStochastic"; oD  = indicatorsData.SStochastic; oData = indicatorsData.SStochastic.data;
           aField = ["Slow Stochastic: %K("+oD.parameter[0]+"):","%D("+oD.parameter[1]+"):"];
           buildSingleIndicatorLegend(sName,oD,oData,aField,index);
        } 
        if (indicatorVisiable.FStochastic) {  
            sName = "FStochastic"; oD  = indicatorsData.FStochastic; oData = indicatorsData.FStochastic.data;
            aField = ["Fast Stochastic: %K("+oD.parameter[0]+"):","%D("+oD.parameter[1]+"):"];
            buildSingleIndicatorLegend(sName,oD,oData,aField,index);
        } 
    }
    
    this.overIndicatorLegend = function(key) {
        outObj.find("[idkey='"+key+"innerL']").addClass("rtc-legend-remove");
    };
    
    this.outIndicatorLegend = function(key) {
        outObj.find("[idkey='"+key+"innerL']").removeClass("rtc-legend-remove");
    };
    
    function showDividendInfo(item) {   
        dividendInfoElm.hide();
        var i = item.dataIndex;
        if (DList == null || DList.length <1|| DividendInfoList == null || DividendInfoList.length < 1) {
            return;
        }    
        var dat,info,flag;
        for (var j = 0; j < DList.length; j++) {
            if (DList[j][0] == i && j < DividendInfoList.length) {
                dat = toDateFromIndex(DList[j][DList[j].length-1]); 
                info = DividendInfoList[j];
                flag=true; break;
            }
        }
        if (flag) {
            dividendInfoElm.html(info+DTFormat.ToLongDate(dat));
            var ppRelate = priceChartContainer.offsetParent().offset();
            var lx = item.pageX-ppRelate.left, tp = item.pageY-20-dividendInfoElm.height()-ppRelate.top;
            if(lx+95 > pChartWidth) lx = pChartWidth-95;
            if(tp < 0) tp = 0;//if(tp<90) tp=90;
            dividendInfoElm.show(); 
            dividendInfoElm.css({left:lx,top:tp});
        }
    }
    
    function getVol(index) {  
      var vo = (timeRangeType == "real")?realTimeVolData:VolList;
      return ((vo == null || index >= vo.length)?"--": vo[index][1]);
    }
    
    function getTicks(list) { 
        var s = new Date();
        if(list==null||list.length<1) return null;
        var lg = list.length;
        var dx = list[lg-1][list[lg-1].length-1] - list[0][list[0].length-1];
        var ty = "d", xx =1;
        var t = [];
        var temp = toDateFromIndex(list[0][list[0].length-1]);
        if (dx < 8) { ty = "d";   t.push([0,DTFormat.ToLongDate(temp)]);
        } else if (dx <= 31) {
            ty ="avg";  t.push([0,DTFormat.ToLongDate(temp)]);
        } else if (dx <= 61) {
            ty="w";  t.push([0,DTFormat.ToLongDate(temp)]);
        } else if (dx <= 240) {
            ty="m"; t.push([0,(temp.getMonth()==0?temp.getFullYear():DTFormat.Mo[temp.getMonth()])]);
        } else if (dx <= 730) {
            ty="q";   t.push([0,(temp.getMonth()==0?temp.getFullYear():DTFormat.Mo[temp.getMonth()])]);
        } else if (dx<= 2920) {
            ty ="y";  t.push([0,temp.getFullYear()]);
        } else { 
            ty = "5y";  t.push([0,temp.getFullYear()]);
        }
        for (var i = 1; i < lg;i+= xx) {
            var d = toDateFromIndex(list[i][list[i].length-1]);
            if ( ty == "d") {
                temp = toDateFromIndex(list[i][list[i].length-1]);t.push([i,DTFormat.ToLongDate(temp)]);
            } else if (ty == "avg") { 
                if (i%4 == 0) { 
                    temp = toDateFromIndex(list[i][list[i].length-1]); t.push([i,DTFormat.ToLongDate(temp)]);
                } 
            } else if (ty == "w") { 
                if (d.getDay() == 5) { 
                    temp = toDateFromIndex(list[i][list[i].length-1]); t.push([i,DTFormat.ToLongDate(temp)]);
                } 
            } else if (ty == "m") { 
                if (temp.getMonth() != d.getMonth()) { 
                    temp = toDateFromIndex(list[i][list[i].length-1]); t.push([i,(temp.getMonth()==0?temp.getFullYear():DTFormat.Mo[temp.getMonth()])]);
                }
            } else if (ty == "q") {
                if (((d.getMonth()+1)%3 == 1) && (temp.getMonth() != d.getMonth())) {
                    temp = toDateFromIndex(list[i][list[i].length-1]);t.push([i,(temp.getMonth()==0?temp.getFullYear():DTFormat.Mo[temp.getMonth()])]);
                } 
            } else if (ty == "y") {
                if (temp.getFullYear() != d.getFullYear()) {
                    temp = toDateFromIndex(list[i][list[i].length-1]);t.push([i,temp.getFullYear()]);
                }
            } else { 
                if ((temp.getFullYear() != d.getFullYear()) && (d.getFullYear()%5==0)) {
                    temp = toDateFromIndex(list[i][list[i].length-1]);t.push([i,temp.getFullYear()]);
                } 
            }
        } 
        var e =  new Date();
        writeTimeLog("calculate ticks ",s,e);
        return t;
    } 
    
    this.clearHighlight = function() {
        if(p != null) {
            p.clearHighlights();
        }
    };
    
    function chartMouseOut() {      
		var t = chartCursor == 1?200:100;
        timeout = setTimeout(function(){chartObj.clearHighlight();mouseOutChart();},t);
    }
    
    function drawChart(container,dataList) { 
        if (dataList == null || dataList.length < 1) { 
            return;
        }
        pChartReady = false;
        mouseOutChart();
        var s= new Date();
        if (timeRangeType != "oneday" && timeRangeType != "fivedays") {
            ticksize = null; Ticks = getTicks(dataList[0].data);
        } else {
           Ticks = null;
        }
        if (p == null) { 
            p = new mstar.flot(priceChartID,getChartOption("priceLine"));
            priceChartContainer.unbind("plothover");
            priceChartContainer.bind("plothover", function(event, pos, item) {if(p.trendLineAble)return ;buildLegend(item,pos,false,false);});
            priceChartContainer.unbind("mouseout");
            priceChartContainer.bind("mouseout", function(e) { 
				var t = e.relatedTarget? e.relatedTarget.nodeName.toLowerCase():"";
				var tt = e.target.nodeName.toLowerCase();
				//alert(t+","+tt);
				if(t=="shape" ||t== "stroke"||tt=="shape"||t=="stroke"){
					return false;
				}
				chartMouseOut();
			} );
			priceChartContainer.bind("filishTrendLine",function(){ //jacky
				$("input[name='addTrendLine']").attr("disabled","");
			});
			//jacky trend line
            priceChartContainer.bind("mouseover", function() {
				if(p.trendLineAble){
        			if (chartCursor == 1) {
            			crosshairLegendElmContainer.hide(); 
            			lineXElm.hide(); lineYElm.hide();
        			}	
					return ;
				}
				chartOffset = priceChartContainer.offset();
				lineXElm.css({width:pChartWidth}); 
				lineYElm.css({height:priceChartContainer.height()});});
        }
        var data = [];
        var dataLs = dataList;
        var mainyaxis = 1;
        if (dataLs == null) {
            return;
        }
        var w = 1, cloIndx;
		if (compairTickers.length > 0 && chartStyle != "lines") {
			mainyaxis = 2;
		}
        for (var i = 0; i < dataLs.length; i++) { 
            w = i == 0?2:1;
			cloIndx = dataLs[i].colorIndex == null ?i:dataLs[i].colorIndex;
			if(dataLs[i].name == "PSAR"){
				data.push(getChartData(dataLs[i].data,"points",mainyaxis,dataLs[i].colorIndex,1));
				continue;
			}
            if (dataLs.length > 1 && chartStyle != "lines") {
                if (i == 0) {
                    data.push(getChartData(dataLs[i].data,chartStyle,2,cloIndx,w));
                } else {
                    data.push(getChartData(dataLs[i].data,"lines",1,cloIndx,w));
                }
            	
            } else {
				if(i == 0&&chartStyle == "lines"){
					data.push(getChartData(dataLs[i].data,"arealine",1,cloIndx,w));
				}else{
					data.push(getChartData(dataLs[i].data,chartStyle,1,cloIndx,w));
				}
                
            }
        }
        if (zoomRange > 2 && (isDividend || isSplits) && DList != null && DList.length > 0) {  //historical data;
            data.push(getChartData(DList,"img",mainyaxis,null)); 
        }
        writeTimeLog("draw price chart start ",null,null);
		var scal = (scaleType == "1" && compairTickers.length > 0)?0: parseInt(scaleType,10);
        p.setOption({xaxis:{tickSize:ticksize,ticks:Ticks},yaxis:{scaleType:scal}});
        p.init(data);
        writeTimeLog("draw price chart end ",null,null);
        var e = new Date();
        writeTimeLog("draw price chart ",s,e);  
        pChartReady = true;  
    }
      
    function drawVol(container,dataList) {
        if(dataList == null || !showVolume) return;
        var s= new Date();
        var data = [];
        var dataLs = [dataList];
        if (dataLs == null) {
            return;
        }
        for (var i = 0;i < dataLs.length; i++) {
            data.push(getChartData(dataLs[i],"bars",1,17));
        }
        if (indicatorsData.SMAV["num"] != null && indicatorsData.SMAV["data"] != null) {
            data.push(getChartData(indicatorsData.SMAV["data"],"lines",1,1,1));
        }
        if (v == null) {
            v = new mstar.flot(volumeChartID,getChartOption("volumeBar"));
        }
        v.setOption({xaxis:{tickSize:ticksize,ticks:Ticks}}); // change the xaisx labels;
        v.init(data);
        var e = new Date();
        writeTimeLog("draw vol chart ",s,e);
    }
    this.removeValuationChart = function(sName){
	 	if (valuations[sName] != null) { // hide the new indicator chart
           outObj.find("[idkey='"+sName+"Legend']").hide();
           outObj.find("[idkey='"+sName+"Chart']").hide();
           if(UIConfig.autoHeight) {
               outHeight -= 78;    //
           }
        }
        if (UIConfig.chartCtrl == "default") {
            $chkbox = outObj.find("li[idkey='valuationList']").find("a[value='"+sName+"']");
        } else {
            $chkbox = $settingPanel.find("li[idkey='valuationList']").find("a[value='"+sName+"']");
        }
        $chkbox.addClass("rtq-chkbox_off");
        valuations[sName].show = false;
		valuations[sName].data = [];
        chartObj.changeOutSize(pChartWidth,outHeight);        
        
	}
	function drawValuationChart(sName){
		if(!valuations[sName] || !valuations[sName].show){
			return;
		}
		if (!valuations[sName].oFlot) {
			var option = getChartOption("indicatorLines");
			var sIdkey = sName + "Chart", sId = outContainerId + "_" + sIdkey;
			var htm = '<div class="rtc-chart-container"><div style="width:100%; height:14px" class="rtc-legend-indicator" idkey="' + sName + 'Legend">' + sName + '</div><div id = "' + sId + '" idkey="' + sIdkey + '" class="rtc-chart"></div></div>';
			if (UIConfig.Chart.showTimeSlider) {
				miniChartContainer.parent().before(htm);
			}
			else {
				outObj.append(htm);
			}
			var $Chart = $("#" + sId);
			$Chart.css({width: pChartWidth + "px",height: "60px"});
			valuations[sName].oFlot = new mstar.flot(sId, option);
		} else {
			outObj.find("[idkey='"+sName+"Legend']").show();
            var $c = outObj.find("[idkey='"+sName+"Chart']"); $c.css({width:pChartWidth+"px"});$c.show();
		}
		if(valuations[sName].data != null){
			valuations[sName].oFlot.setOption({xaxis:{tickSize:ticksize,ticks:Ticks}});
			valuations[sName].oFlot.init([valuations[sName].data])
		}
	}
    function drawIndicatorChart() { 
         if(indicatorVisiable.MACD) drawSingleIndicatorChart("MACD");
         if(indicatorVisiable.RSI)  drawSingleIndicatorChart("RSI");
         if(indicatorVisiable.ROC) drawSingleIndicatorChart("ROC");
         if(indicatorVisiable.WillR) drawSingleIndicatorChart("WillR");
         if(indicatorVisiable.SStochastic) drawSingleIndicatorChart("SStochastic");
         if(indicatorVisiable.FStochastic) drawSingleIndicatorChart("FStochastic");
    }
    
    function drawSingleIndicatorChart(sName) {
        if (indicatorVisiable[sName] && typeof(indicatorFlots[sName]) != "undefined") { // new charts
            if (indicatorFlots[sName] == null) { 
                var option = getChartOption("indicatorLines");
                if (sName == "WillR") {
                    option.yaxis.ticks =  [-50,-100]; // specil the yaxis for willR indicator chart.
                }
                var sIdkey = sName+"Chart", sId = outContainerId+"_"+sIdkey;
				var htm = '<div class="rtc-chart-container"><div style="width:100%; height:14px" class="rtc-legend-indicator" idkey="'+sName+'Legend">'+sName+'</div><div id = "'+sId+'" idkey="'+sIdkey+'" class="rtc-chart"></div></div>';
                if(UIConfig.Chart.showTimeSlider){
					miniChartContainer.parent().before(htm);
				}else{
					outObj.append(htm);
				}	
                var $Chart = $("#"+sId); $Chart.css({width:pChartWidth+"px",height:"60px"}); 
                indicatorFlots[sName] = new mstar.flot(sId,option);
            } else {
                outObj.find("[idkey='"+sName+"Legend']").show();
                var $c = outObj.find("[idkey='"+sName+"Chart']"); $c.css({width:pChartWidth+"px"});$c.show();
            }
            if (indicatorsData[sName].data != null ) {
                indicatorFlots[sName].setOption({xaxis:{tickSize:ticksize,ticks:Ticks}});
                if (sName == "MACD") { 
                    var aChartData = []; var aIndData = indicatorsData[sName].data; aChartData[0] = aIndData[0]; aChartData[1] = aIndData[1]; aChartData[2] = getChartData(aIndData[2],"bars",1,null);
                    indicatorFlots[sName].init(aChartData);
                } else {
                    indicatorFlots[sName].init(indicatorsData[sName].data);
                }
            }
        }
    }
    
    function  writeTimeLog(info,s,e) { 
     /*if(s==null)
        $("#log").html( $("#log").html()+"<br> "+info);
       else  $("#log").html( $("#log").html()+"<br> "+info+s.toLocaleTimeString() +" to "+e.toLocaleTimeString() +" use "+(e.getTime()-s.getTime())+" ms");*/
    }
    
    function drawMiniChart() {
        if (UIConfig.Chart == null || (!UIConfig.Chart.showTimeSlider && !UIConfig.CustomUI)) {
            return;
        }
        if(m == null) {
            m= new mstar.flot(miniChartID,getChartOption("miniChart"));    
        }
        var s= new Date();
        m.setOption({xaxis:{tickSize:getMiniTickSize(),ticks:null}});
        m.init(MiniChartData);
        var e = new Date();
        writeTimeLog("draw miniChart ",s,e);
    }
    
    this.removeTicker = function (i) { 
		if(i < 0 || i >= compairTickers.length) return;
		if(indexsMap[compairTickers[i]]){
			indexsULElm.find("a[value='"+compairTickers[i]+"']").addClass("rtq-chkbox_off");
		}
        bacthUnscribeTickers([compairTickers[i]]);
		delete tickerInfos[compairTickers[i]];   
        compairTickers.splice(i,1);
        Draw(false);   
    };
      
    this.removeSMV = function(i) {
        if (indicatorsData.SMA && i < indicatorsData.SMA["num"].length) { 
			indicatorsData.SMA["num"][i] = null;
			indicatorsData.SMA["data"][i] = null;
			indicatorsData.SMA["startPos"][i] = null;
            if (UIConfig.chartCtrl == "default") {
                outObj.find("[idkey='mov"+(i+1)+"']").attr("value","");
            } else {
                 $settingPanel.find("[idkey='mov"+(i+1)+"']").attr("value","");
            }
			for(var j=0; j< DataList.length;j++ ){
				if(DataList[j].name == "SMA" && DataList[j].sq == i){
					DataList.splice(j,1);
					break;
				}
			}
            drawChart(priceChartContainer,DataList);
        }
    };
      
    this.removeSMVV =  function () {
        if (indicatorsData.SMAV) {
            indicatorsData.SMAV["num"] = null;
			indicatorsData.SMAV["data"] = null;
			indicatorsData.SMAV["startPos"] = null;
            if (UIConfig.chartCtrl == "default") {
                outObj.find("[idkey=movVol]").attr("value","");
            } else {
                $settingPanel.find("[idkey=movVol]").attr("value","");
            }
            mouseOutChart(); drawVol(volChartContainer,VolList);
        }
    };
      
    function addSecurity() {
        var tic = $("#"+autoBoxID).attr("value");
        if (tic != null && tic != "") {
			validateCompairTicker(tic);
            //checkTickerInfo(tic,objName+".SecruritySelected");
        }
    }
    
    function checkCompairTicker(t) {
        if (t.toUpperCase() == mainTicker.toUpperCase()) {
            return true;
        }
    	for (var i = 0; i < compairTickers.length; i++) {
    	    if (compairTickers[i].toUpperCase() == t.toUpperCase()) {
    	        return true;
    	    }
        }
        return false;
    }
    
    function toStockTypeCode(ty) { 
        if ( ty == null) return "1";
        var t = ty.toLocaleUpperCase()
        if (t == "ST" || t == "FE") {
            return "1";
        } else if (t=="XI") {
            return "10";      
        } else {
            return "1";
        }    
    }
      // check the ticker and return the stock type.
    function checkTickerInfo(symbol,callback) {
    	if (symbol == "") {
    	    return false;
    	}    
    	var url = getTickerInfoURL + "symbol=" + symbol + "&region=" + region + "&cb="+callback;
    	$.getScript(url);
    }
   
    
    this.selectSecurity = function (line){ 
        if (line != null && line != "") {
            checkTickerInfo(line,objName+".SecruritySelected");
        } 
    };
      
    function clearBox(obj) {
        if (obj == null) {
            return;
        }
        var v=$(obj).val(); 
        if(v.substring(0,4) == "Line" || v== "Enter Security") {
            $(obj).val("");$(obj).css("color","#333333");
        }
    }
    
    function addSMV(num,va) { 
        var n = parseInt(va);
		var sma = indicatorsData["SMA"];
        if (n > 0 && sma["num"] && num < sma["num"].length) { 
            if (sma["num"][num] == null || sma["num"][num] != n) {
                sma["num"][num] = n;
				sma["data"][num] = null;
                if (timeRangeType == "oneday" || timeRangeType == "fivedays") {
                    filterIntradeData(true);
                } else {
                    filterHistoricalData();
                }
            drawChart(priceChartContainer,DataList);}
        } 
    }
    
    function addSMVV(va) { 
        var n = parseInt(va);
        if(isNaN(n) || n<= 0) {
            return;
        }
		var smav = indicatorsData["SMAV"];
        if (smav && smav.num != n) { 
            smav.num = n;
			smav.data = null;
            if (timeRangeType == "oneday" || timeRangeType == "fivedays") {
                filterIntradeData(false);
            } else { 
              filterHistoricalData();
            }
            drawVol(volChartContainer,VolList);
            mouseOutChart();
        }
    }
      
    function toggleVolumeChartVision() {
        if (showVolume) {
            showVolume = false;
            if (UIConfig.chartCtrl == "default") lin.html("Show");
            pChartHeight = pChartHeight+vChartHeight; vChartHeight=0; 
            priceChartContainer.css("height",pChartHeight); 
            volChartContainer.css("height",vChartHeight); 
            if(p != null) {
                p.changeOutSize(pChartWidth,pChartHeight);
            }
            volChartContainer.hide();
        } else if (!showVolume) {
            showVolume = true;
            if (UIConfig.chartCtrl == "default") lin.html("Hide");
            volChartContainer.show();
            vChartHeight=pChartHeight*0.2;
            pChartHeight = pChartHeight*0.8; 
            priceChartContainer.css("height",pChartHeight); 
            volChartContainer.css("height",vChartHeight);
            if(p != null) {
                p.changeOutSize(pChartWidth,pChartHeight);
            }
            if(v != null) {
                v.changeOutSize(pChartWidth,vChartHeight);
            }
        }
    }
    
    function changeClassName(obj, cName) { // fix IE6 bug.
        if (obj == null) {
            return;
        }    
        if (cName == 'over') {
            obj.className += " over";
        } else if (cName == 'out') {
            obj.className = obj.className.replace(/ over/gi, "");
        }
    }
    
    function changeStyle(obj, st) {
        if (obj == null) {
            return;
        }
        if (st == 'display') {
            obj.childNodes[1].style.display = "block";
        } else if (st == 'none') {
            obj.childNodes[1].style.display = "none";
        }   
    }
    
    function zoomSelected(t,v) {
        changeRange(parseInt(v));
        return false;
    }
    function frequencySelected(k,v){
        if (frequency != v) { 
            frequency = v;
            if (rightMenuElm != null) { 
                rightMenuElm.find("[idkey='freul']").find("a").removeClass("on");
                rightMenuElm.find("a[value='"+v+"']").addClass("on");
            }
            Draw(true);
        }
    }
    function cursorTypeSelected(k,v){
        chartCursor = parseInt(v,10);
        if (rightMenuElm != null) { 
                rightMenuElm.find("[idkey='divCursorType']").find("a").removeClass("on");
                rightMenuElm.find("a[value='"+v+"']").addClass("on");
        }
        if (chartCursor == 3) {
            priceChartContainer.unbind("plothover");
        } else {
			crosshairLegendElmContainer.hide();
   			lineXElm.hide(), lineYElm.hide();
            priceChartContainer.unbind("plothover");
            priceChartContainer.bind("plothover", function(event, pos, item) {buildLegend(item,pos,false,false);});
        }
    }
    function chartTypeSelected(k,v){
        if (chartStyle != v) {
            chartStyle = v;
            if (rightMenuElm != null&&rightMenuElm.length > 0) { 
                rightMenuElm.find("[idkey='divPlotStyle']").find("a").removeClass("on");
                rightMenuElm.find("a[value='"+v+"']").addClass("on");
            }
			if(UIConfig.chartCtrl == "rtqPanel" && tabMenu != null){
				var i = v=="lines"?0:(v=="candle"?2:1);
				tabMenu.changeTabByIndex(i,false);
			}
            if ((timeRangeType == "oneday" || timeRangeType == "fivedays") && (chartStyle == "candle" || chartStyle == "ohlc")) { //change to one month.
                if (ddlZoom != null)ddlZoom.setTextValue("","3");
                changeRange(3);
            } else {
                drawHisChart();
            }
			if(typeof(chartObj.afterChartTypeChanged)== "function"){
				chartObj.afterChartTypeChanged({chartType:chartStyle});
			}
        }
    }
	function scaleTypeSelected(k,v){
		if(scaleType != v){
			scaleType = v;
			drawChart(priceChartContainer,DataList);// only draw main chart.
		}
	}
    function menuButtonClicked(obj)  {              // menu item clicked
        writeTimeLog("menu Clicked",null,null);
        var catKey = $(obj).attr("catkey");
        var va = $(obj).attr("value");
        if (catKey == "chartType" && ddlChartType != null) {
            ddlChartType.selectItemByValue(va);
            return false; 
        } else if (catKey == "frequency" && ddlFrequency != null) { // change frequency
            ddlFrequency.selectItemByValue(va);
            return false;
        } else if(catKey == "cursorType" && ddlCursorType != null) {
            ddlCursorType.selectItemByValue(va);
            return false;
        } else if (catKey == "zoom") {
            ddlZoom.selectItemByValue(va);
            return false;
        }else if(catKey == "scale"){
			ddlScaleType.selectItemByValue(va);
            return false;
		}else if (catKey == "event") {
            eventULElm.find("a[value='"+va+"']").toggleClass("rtq-chkbox_off");
            if (rightMenuElm != null) { 
                rightMenuElm.find("a[catKey == 'event']").find("a[value='"+va+"']").toggleClass("rtq-chkbox_off"); 
            }
            if (va == "dividend") { 
                isDividend = !$(obj).hasClass("rtq-chkbox_off");
            } else if (va == "splits") {
                isSplits = !$(obj).hasClass("rtq-chkbox_off");
            } 
            if(zoomRange > 2) {
                Draw(false); 
            }      
        } else if (catKey == "indicator") { 
            showIndicatorWin(va);       
        } else if(catKey == "indexs"){
			indexsULElm.find("a[value='"+va+"']").toggleClass("rtq-chkbox_off");
			indexsMap[va]= !$(obj).hasClass("rtq-chkbox_off");
			if(indexsMap[va]){
				chartObj.addCompairTickers(va);
			} else {
				chartObj.removeCompairTicker(va);
			}
		} else if (catKey == "valuation"){ // draw valuation chart.
			valuationULElm.find("a[value='"+va+"']").toggleClass("rtq-chkbox_off");
			if(valuations[va]){
				valuations[va].show = !$(obj).hasClass("rtq-chkbox_off");
				if(valuations[va].show){
					if(UIConfig.autoHeight && !valuations[va].show) {
		                outHeight += 78;
			        }
			        setSize(pChartWidth,outHeight);
			        if(p != null){ 
			            p.resizeCanvas(pChartWidth,pChartHeight);
			        }
			        if(v != null && showVolume) {
			            v.resizeCanvas(pChartWidth,vChartHeight);
			        }
					Draw(false);
					drawValuation(va);
				}else {  
					chartObj.removeValuationChart(va);
				}
			}
		}
    }
    
    function removeIndicatorClicked(obj) { 
        var $bt = indicatorWinElm.find("[idkey='removeBt']");
        var sName =  $bt.attr("indicatorName");
        indicatorVisiable[sName] = false;
        indicatorWinElm.hide();
        chartObj.removeIndicators(sName);
        Draw(true);   
        pChartReady = true;   // mouse can over chart
    }
     
    function showIndicatorWin(name) {
        if (indicatorVisiable[name] == null || indicatorUI[name] == null) {
            return;
        }
        var oInd = indicatorUI[name];
        pChartReady = false;                  // stop mouse over  event.
        indicatorWinElm.show();
        indicatorWinElm.find("[idkey='title']").html(oInd.title);
        var sStr = ""; var aP = oInd.parameter; var aDv = oInd.defaultValue; var oIndData = indicatorsData[name];
        if (aP != null) { 
            var iV ;
            for (var i = 0; i < aP.length; i++){  
               if (name == "EMA") {
                   iV = oIndData.num[i] == null?(aDv[i]==null?"":aDv[i]):(oIndData.num[i]==null?"":oIndData.num[i]);
               } else {
                   iV = (oIndData.parameter[i] == null?(aDv[i]==null?"":aDv[i]):(oIndData.parameter[i] ==null?"":oIndData.parameter[i]));
               }            
               sStr += '<dl><dt>'+aP[i]+'</dt><dd><input type="text" idkey="p'+i+'" class="qs-ui-ipt" value="'+iV+'" /></dd></dl>';
            }
            sStr+='<br style="clear:both;" />'
        }
        indicatorWinElm.find("[idkey='content']").html(sStr);
        var $bt = indicatorWinElm.find("[idkey='drawBt']");
        var $rt = indicatorWinElm.find("[idkey='removeBt']");
        $bt.attr("indicatorName",name);
        $rt.attr("indicatorName",name);
        if (indicatorVisiable[name]) {
            $rt.show();
        } else {
            $rt.hide();
        }
    }
    
    function setIndicatorParas(sName) {
        var sp, ip, isDraw = false;
        var oPa = indicatorsData[sName];
        var hasInput = arguments.length <= 1;
        if (oPa != null && sName != "EMA") {
            for (var i = 0 ; i < oPa.parameter.length; i++) {
                sP = hasInput?indicatorWinElm.find("[idkey='p"+i+"']").val():arguments[i+1]; 
                iP = parseFloat(sP);
                if (!isNaN(iP)) {
                    oPa.parameter[i] = iP;
                } else {
                    oPa.parameter[i] = indicatorUI[sName].defaultValue[i];
                }
                isDraw = true;
            }
        } else if (oPa != null && sName == "EMA") {
            for (var j = 0; j < oPa.num.length; j++) {
                sP = hasInput?indicatorWinElm.find("[idkey='p"+j+"']").val():arguments[j+1];
                iP = parseFloat(sP);
                if (!isNaN(iP)) {
                    oPa.num[j] = iP;
                    isDraw = true;
                } else {
                    oPa.num[j] = null;
                }
            }
        }
        return isDraw;
    }
    function drawIndicator(obj) { 
        var $bt = indicatorWinElm.find("[idkey='drawBt']");
        var sName =  $bt.attr("indicatorName");
        var isDraw = setIndicatorParas(sName); 
        var $chkbox;
        indicatorWinElm.hide();
		var isExsitedInd = indicatorVisiable[sName];
        indicatorVisiable[sName] = true;
        if (typeof(indicatorFlots[sName]) != "undefined") {
            if(UIConfig.autoHeight && !isExsitedInd) {  // not update parameter.
                outHeight += 78;
            }
            setSize(pChartWidth,outHeight);
            if(p != null){ 
                p.resizeCanvas(pChartWidth,pChartHeight);
            }
            if(v != null && showVolume) {
                v.resizeCanvas(pChartWidth,vChartHeight);
            }
            Draw(true); // only filter indicator data
            if (UIConfig.Chart && (UIConfig.Chart.showTimeSlider || UIConfig.CustomUI)) {
                drawMiniChart(); 
                //setTimeout(function(){chartObj.resetSlider();},100);
            }
        } else { 
            Draw(false); // draw overlay chart.
        }
        if (UIConfig.chartCtrl == "default") {
            $chkbox = outObj.find("li[idkey='indicatorList']").find("a[value='"+sName+"']");
        } else {
            $chkbox = $settingPanel.find("li[idkey='indicatorList']").find("a[value='"+sName+"']");
        }
        if(isDraw){
            $chkbox.removeClass("rtq-chkbox_off");
        } else {
            $chkbox.addClass("rtq-chkbox_off");
        }
        pChartReady = true;
        buildIndicatorLegend(-1);
    }
    
    function cancelIndicator(obj) { 
        pChartReady = true;
        indicatorWinElm.hide();
        indicatorWinElm.find("[idkey='title']").html("");
        indicatorWinElm.find("[idkey='content']").html("");
    }
    
    // slider function         
    function getSliderStartDate() {     //get slider start date.
        if (frequency == "minute" || frequency == "d") {
            return dailyStartDate;
        } else if (frequency == "w") {
            return weeklyStartDate;
        } else if (frequency == "m") {
            return monthlyStartDate;
        } else {
            return new Date(1900,0,1);
        }
    }   
    
    function toDate(pos) {  // convert the slider position to date.
        if (pos == 0) {
            return slider_StartDate;
        } else { 
            var dx;
            if (miniChartFrequency == "m") {
                var dm = slider_EndDate.getMonth() - slider_StartDate.getMonth() + 1;
                var dy = slider_EndDate.getFullYear() - slider_StartDate.getFullYear();
                dx = rangSize / (dy * 12 + dm);
                var dYear = (rangSize - pos) / (12 * dx);
                var ye = Math.floor(dYear);
                var mon = Math.round((dYear - ye) * 12);
                curDate = new Date(slider_EndDate.getFullYear() - ye, slider_EndDate.getMonth()-mon, 1);
                return curDate;
            } else if (miniChartFrequency == "d"||miniChartFrequency=="w") {
                var totalTicks = slider_EndDate.getTime()-slider_StartDate.getTime();
                if (totalTicks == 0) { 
                    return trimDate(latestTradeDate); 
                }
                dx = totalTicks * (rangSize - pos) / rangSize;       
                curDate = new Date (slider_EndDate.getTime()-dx);
                return curDate
           }
        }
    }
    
    function toPos(curdate) {  //convert the date to slider postion.
        if (curdate == null) {
            return 0;
        }    
        var pos = 0;
        if (miniChartFrequency == "m") {
            var yd = slider_EndDate.getFullYear() - curdate.getFullYear();
            var md = slider_EndDate.getMonth() - curdate.getMonth();
            var dx = yd * 12 + md;
            var dm = slider_EndDate.getMonth() - slider_StartDate.getMonth() + 1;
            var dy = slider_EndDate.getFullYear() - slider_StartDate.getFullYear();
            pos = Math.round(rangSize - rangSize * dx / (dy * 12 + dm));
        } else if (miniChartFrequency == "d" || miniChartFrequency == "w") {
            var totalTicks = slider_EndDate.getTime()-slider_StartDate.getTime();
            var dTicks = slider_EndDate.getTime()-curdate.getTime();
            if (totalTicks == 0) {
                return 0;
            }
            pos = Math.ceil(rangSize-rangSize*dTicks/totalTicks);
        }
        if (pos > rangSize) {
            return rangSize;
        } else if (pos < 0) {
            return 0;
        } else {
            return pos;
        }
    }    
    
    //this.resetSlider = createSlider;
    
    this.setSliderHandleValues = function() { // reset the slider handlers postion.
        if (sliderObj != null) {
            sliderObj.setOption({values:[toPos(startDate),toPos(endDate)]});
        }
    };
         
    function createSlider() { // reset the slider control.
        if (miniChartContainer.length < 1) return;
        var PosL = toPos(startDate);
        var PosR = toPos(endDate);
        if(sliderObj == null) {
            sliderObj = new Util.Silder(miniChartContainer[0],{min:0,max:rangSize, values:[PosL,PosR]});
            sliderObj.onchange = sliderRangeOnChange;
            sliderObj.onchanged = sliderRangeOnChanged;
        }
    }
    
    function sliderRangeOnChange(values) { // handler the silder changing event
        if (zoomRange != 11) {  // convert to custom
            zoomRange = 11;
            if (ddlZoom) {
                ddlZoom.setTextValue("Custom", "11");
            }
            if (rightMenuElm != null && rightMenuElm.length > 0) {
                rightMenuElm.find("UL[idkey='timeRange']").find("a").removeClass("on");
            }
        }
        var s = toDate(values[0]);
        var e = toDate(values[1]);
        setTimeRange(s, e);
    }
        
    function sliderRangeOnChanged(values) { // loading chart and data when the slider has moved.
        var s = toDate(values[0]);
        var e = toDate(values[1]);
        startDate = s, endDate = e;
        timeRangeType = "custom";
        var  f = calculateFrequency(startDate);
        changeFrequency(f);  
        Draw(true);
		if(typeof(chartObj.datePeriodChanged) == "function" ){
			chartObj.datePeriodChanged({timeRangeType:timeRangeType,zoomRange:zoomRange,startDate:formatDate(s),endDate:formatDate(e)});
		}
        var ff = miniChartFrequency;   // convert minichart view.
        if (values[0] <= 1) { 
            ff = upMiniFrequency(); //when slider move to the left.
        } else if (convertToNumber(f) < convertToNumber(miniChartFrequency)) {
        ff = f; // zoom in minichart view.
        }
        if (miniChartFrequency != ff) {
            miniChartFrequency = ff; 
            DrawMiniChart();
            chartObj.setSliderHandleValues();
        }
    }
    
    function setTimeRange(s, e) { // change the slider handler position according the start date and end date.
        if (s == null || e == null) {
            return;
        }
        setTimeRangeBox(s,e);
    }
        
    function upMiniFrequency() {  
        var f;
        if (miniChartFrequency == "d") {
            f = "w";
        } else if (miniChartFrequency == "w") {
            f = "m";
        } else {
            f ="m";
        }
        return f;
    }
     
    //Date Util functions
        
    function trimDate(date) { 
        return new Date(date.getFullYear(), date.getMonth(), date.getDate());
    }
    
    function toDateIndex(dt) { // days from 1900-1-1;
        if (dt == null) {
            return 0;
        }
        var st = new Date(1900, 0, 1).getTime(); 
        var end= new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()).getTime();
        return (end-st)/(24 * 60 * 60 * 1000)
    }
     
    function toDateFromIndex(index) {
        return new Date (dateIndexStart+index* dateIndexBase);
    }
     
    function formatDate(date) {     //formate date to yyyy-mm-dd
        if (date == null) {
            return null;
        }
        return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); 
    }  
     
    function convertToDate(d) {    // convert yy-mm-dd to date format.
        if (d == null || d =="") {
            return null;
        }
        var f = d.split('-');
        var f1 = f[0], f2 = parseInt(f[1], 10), f3 = parseInt(f[2], 10);
        return new Date(f1, f2 - 1, f3);
    }
    
    function compareDate(from, to) {  // comapair the date 
        var f = from.split('-'), t = to.split('-'),  f1 = f[0],  f2 = parseInt(f[1], 10), f3 = parseInt(f[2], 10);
        var  t1 = t[0],  t2 = parseInt(t[1], 10),   t3 = parseInt(t[2], 10);     
        return  f1<t1? true:(f1 == t1?(f2 < t2?true:(f2 == t2?(f3 < t3?true:false):false)):false);
    }
    
    function isVbDate(str) { //check the date format yyyy-mm-dd.
         var reg = /^(\d{4})(-|\/|\.)(\d{1,2})\2(\d{1,2})$/;
         var result = str.match(reg);
         if (result == null) return false; 
         var y, m, d;
         y = result[1]; //year number
         m = parseInt(result[3], 10); //month number
         d = parseInt(result[4], 10); //day 
         if ((m < 1) || (m > 12) || (d < 1) || (d > 31)) return false;
         if (((m == 4) || (m == 6) || (m == 9) || (m == 11)) && (d > 30)) return false;
         if ((y % 4) == 0) {
            if ((m == 2) && (d > 29)) return false;
         } else {
            if ((m == 2) && (d > 28)) return false;
         }
         return true;
    }
       
     // custom date range.
    function rollbackColor() {
        if (startBoxElm != null && endBoxElm!= null && !dateFromFocus && !dateToFocus) {
            document.body.focus();
            startBoxElm.css({backgroundColor:""}); endBoxElm.css({backgroundColor:""});
            if (document.activeElement.tagName.toUpperCase() != "INPUT") {
                   setTimeRangeBox(startDate,endDate);
            }
        }
    }
    
    function loadCustomChart() {
        if (startBoxElm == null || endBoxElm == null) return false;
        var frm = startBoxElm.attr("value"), tto = endBoxElm.attr("value");
        if (isVbDate(frm) && isVbDate(tto) && compareDate(frm, tto)) {
            var ff = convertToDate(frm); var tt = convertToDate(tto);
            var st = startDate, en = endDate;
            if (st.getFullYear() == ff.getFullYear() && st.getMonth() == ff.getMonth() && st.getDate() == ff.getDate() && en.getFullYear() == tt.getFullYear() && en.getMonth() == tt.getMonth() && en.getDate() == tt.getDate()) {
                rollbackColor();
                return;
            }
            startDate = ff;
            endDate = tt;
            timeRangeType = "custom";
            changeRange(11); // 
        } else {
            rollbackColor();
        }
    }
     
    function changeBack(e) { 
         if (startBoxElm != null && endBoxElm != null) {
             startBoxElm.css({backgroundColor:"#ffff99"});endBoxElm.css({backgroundColor:"#ffff99"});
         } 
    }
    
    function customDateChanged(e) {
        if (dateFromFocus || dateToFocus) {
            return false; // don't do anything.
        } else {
            loadCustomChart();
        }
    }
    
    function clickCustomDate(w) {
        if (w == 0) dateFromFocus = true;
        if (w == 1) dateToFocus = true;
        if (startBoxElm!= null && endBoxElm != null) {
            if (isVbDate(startBoxElm.attr("value"))) startDate = convertToDate(startBoxElm.attr("value"));  //record the changed date
            if (isVbDate(endBoxElm.attr("value"))) endDate = convertToDate(endBoxElm.attr("value"));  //record the changed date.
            var st = startDate, en = endDate;
            var fv= (st == null) ? "" : st.getFullYear() + "-" + (st.getMonth() + 1) + "-" + st.getDate();
            var tv = (en == null) ? "" : en.getFullYear() + "-" + (en.getMonth() + 1) + "-" + en.getDate();
            startBoxElm.attr("value",fv); endBoxElm.attr("value",tv);
            if (document.all) {                         //ie move the foucs to end of text
                var esrc = event.srcElement;
                var rtextRange = esrc.createTextRange();
                rtextRange.moveStart('character', esrc.value.length);
                rtextRange.collapse(true);
                rtextRange.select();
            }
        }
    }
    
    this.customDateLoseFocus = function(w) {
        if (w == 0) dateFromFocus = false;
        else if (w == 1) dateToFocus = false;
        if (!dateFromFocus && !dateToFocus) loadCustomChart();
    };
    
    function checkDateBoxFunKey(kp) {
        var keyID = (window.event) ? kp.keyCode : kp.which ? kp.which : kp.charCode;
        switch (keyID) {
            case 13: loadCustomChart();
                	break;        // Enter key			
        }
    }
        
    this.initCustomTimeRangeBox = function(s, e) {
        setTimeRangeBox(s,e);
        if (typeof(customRangeElm) != "undefined" && customRangeElm != null) {
            customRangeElm.hover(function(e) { changeBack(e) }, function(e) {customDateChanged(e); });
        }
        if (typeof(startBoxElm) != "undefined" && startBoxElm != null) {
            startBoxElm.bind("focus", function() {clickCustomDate(0); });
            startBoxElm.bind("blur", function() { window.setTimeout(objName+'.customDateLoseFocus(0)') });
            startBoxElm.bind("keydown",function(kp){checkDateBoxFunKey(kp);});
       }
       if (typeof(endBoxElm) != "undefined" && endBoxElm != null) {
           endBoxElm.bind("focus", function() { clickCustomDate(1) });
           endBoxElm.bind("blur", function() { window.setTimeout(objName+'.customDateLoseFocus(1)') });
           endBoxElm.bind("keydown",function(kp){checkDateBoxFunKey(kp);});
       }
    };
        
        // intrade Data
    function convertExchange(exch) {
        if (exch.toUpperCase() == "XSHG" || exch.toUpperCase() == "") { //
            return "shanghai stock exchange";
        } else if (exch.toUpperCase() == "XNAS" || exch.toUpperCase() == "XNYS") {
            return "US composite";
        } else if (exch.toUpperCase() == "XSHE") {
            return "Shenzhen Stock Exchange";
        } else if (exch.toUpperCase() == "XLON" || exch.toUpperCase() == "LSE") {
            return "LSE SEAQ national";
        } else {
            return "US composite";
        }
    }
    
    function requestIntrayData(pid,num) {   //
		var info = getTickerInfo(pid);
		if(!info) return;
		var exch;
		if(info.country == "USA"){
			exch = "126";
		} else {
			exch = info.tenforeCode;
		}
		var ty = "D";
		var url= intradayDataUrl+"sId="+Cookies.get("SessionID")+"&ty="+ty+"&exch="+exch+"&stype="+info.type+"&ticker="+info.tenforeTicker+"&days="+num+"&jsoncallback="+objName+".processIntrayData";
		$.getScript(url);
    }
     
    function initDataArray(start,end) { 
        var pointNum = end-start+1;
        var a = new Array(pointNum);
        for (var i = 0;i < pointNum; i++) { var dp = [];
         dp.push(i);  dp.push(NaN); dp.push(start+i);
         a[i] =dp;  
        }
        return a;
    }
     
    this.processIntrayData = function(aData) {
        if (aData == null) return;
        var data = aData.data;
        if (data == "-1") {
           if (hasConfirmBox) {
               Util.confirm("Connection is expired, please login again.",function(bFlg){if (bFlg){window.location.href = sessionExpireRedirectUrl;} else return false; });                                                                                              
           } 
           return;                        // sessionID is Invalid.
        } else if (data == "-2") {
           return;                    //  no permission to access real-time data.
        } else if (data == "-3") {  // pass invalid type parameter.
           return;
        } else if(data ==null || data.length < 1) {
           return ;   // no intraday data.
        }
        var ticker =data[0].exchangeId+":"+data[0].symbol.toUpperCase();
		var pid = getPidByExchAndTicker(ticker);
        var dat;
        if (pid == mainTicker) {
           dat = SecurityData[mainTicker].intradyDayData; 
        } else if(compairTickers != null && compairTickers.length > 0){
           for (var d = 0; d < compairTickers.length;d++){
               if (compairTickers[d] == pid) {
                  dat = SecurityData[compairTickers[d]].intradyDayData;
                  break;
               }
           }
        }
        if (dat == null) return;
        var da= data[0].lastPrice;
        var volData = data[0].volume;
        var date =convertToDate(data[0].date);
        dat.OpenTime = data[0].startTime;
        dat.CloseTime = data[0].endTime;
        dat.LatestYestClose = data[0].yesterdayClosePrice;
        dat.LatestDate = date;
        latestTradeDate = date;
        endDate = date;
        if (zoomRange == 1) startDate = endDate;
        var priceList = da.split(",");//eval("["+ da+"]");
        var vlist = volData.split(",");//eval("["+ volData+"]");
        var ls = StoreIntradeData(priceList,vlist,dat.OpenTime,dat.CloseTime);
        dat.LatestDayData = ls[0];
        dat.LatestDayVolumeData = ls[1];
        if (data.length > 1) { 
            dat.HisData = new Array(data.length-1);
            dat.HisVolumeData = new Array(data.length-1);
            dat.HisYestClose = new Array(data.length-1);
            dat.HisDate = [];
            for (var i = 1;i < data.length; i++) { 
                var plist = data[i].lastPrice.split(",");
                var vollist = data[i].volume.split(",");
                var d =convertToDate(data[i].date);
                dat.HisDate.push(d);
                dat.HisYestClose[i-1]=data[i].yesterdayClosePrice;
                var lss = StoreIntradeData(plist,vollist,dat.OpenTime,dat.CloseTime);
                dat.HisData[i-1] = lss[0];
                dat.HisVolumeData[i-1] = lss[1];
            }  
        }
        if (timeRangeType == "oneday" || timeRangeType == "fivedays") {
            Draw(true);
        }
    };
    
    function StoreIntradeData(priceList,vlist,ot,et) {
       var tpData = [], tvdata = [];
       var pointNum = et-ot+1;
       //tpData= initDataArray(ot,et);
      // tvdata = initDataArray(ot,et);
      var pl = priceList.length, vl = vlist.length;
       for (var j=0;j<pointNum;j++) {
           if (j < pl && priceList[j] != "") {
               tpData[j] = [j,parseFloat(priceList[j]),ot+j]; // store price data.
           } else {
               tpData[j] = [j,NaN,ot+j];
           }
           if (j < vl && vlist[j] != "") {
               tvdata[j] = [j,parseInt(vlist[j],10),ot+j]; // store volume data
           } else {
               tvdata[j] = [j,NaN,ot+j];
           }
       }
       return [tpData,tvdata];
    }
      
    function cacheIntradeData(val,tick,symbol,ty) { 
        if (SecurityData == null) return;
        var t, tic;
        for (var ky in SecurityData) { 
            tic = typeof(gIdMap)!= "undefined" ?gIdMap[ky]:tickerInfos[ky]["gkey"];
            //tic = (t.length>1?t[1]:t[0]);
            if (tic == symbol) {
                var dat;
                if (SecurityData[ky]==null||SecurityData[ky].intradyDayData==null) return;
                if (ty == "p") {
                    dat = SecurityData[ky].intradyDayData.CachedData;// update price. 
                } else {
                    dat = SecurityData[ky].intradyDayData.VolumeCachedData;
                }
                var dp = [];
                if (dat != null && dat.length > 0) {
                    var ddp= dat[dat.length-1];
                    if (ddp != null && ddp[1] != tick) pushIntrayData(ky,ty);
                }
                dp.push(val);
                dp.push(tick);
                if (dat == null) dat = [];
                dat.push(dp);
                break;    
            }
        }
    }
       
    function pushIntrayData(ky,ty) {// push the cache data to intrade data.
        var cdata,data;
        if (SecurityData == null && SecurityData[ky] == null) return;
        if (ty == "p") {
            cdata = SecurityData[ky].intradyDayData.CachedData;
            data = SecurityData[ky].intradyDayData.LatestDayData; // update price. 
        } else {
            cdata = SecurityData[ky].intradyDayData.VolumeCachedData;
            data=SecurityData[ky].intradyDayData.LatestDayVolumeData;
        }
        if (cdata == null || cdata.length == 0) return;
        if(data == null) data = [];
        var tick = cdata[cdata.length-1][1];
        var st = SecurityData[ky].intradyDayData.OpenTime;
        var ed = SecurityData[ky].intradyDayData.CloseTime;
        var index = tick-st;
        if (index < 0 || tick > ed) return;
        if(index == 0 && ty == "p") {   // open time refresh data.
            var list = ky.split(":");
            requestIntrayData(ky,5);  
        } else {
            data[index] = [];
            data[index].push(index);
            for (var i = 0;i < cdata[cdata.length-1].length; i++) {
                data[index].push(cdata[cdata.length-1][i]);
            }
            if (timeRangeType == "oneday" || timeRangeType == "fivedays") {
                Draw(true); //intrad data update redraw the chart.
            }
        }
        cdata.length = 0;   // clear cached data.
    }
    
    function convertToExchCode(exch) {
        if (exch.toUpperCase() == "XSHG") {
            return "136";
        } else if (exch.toUpperCase() == "XNAS" || exch.toUpperCase() == "XNYS" || exch == "XASE") {
            return "126";
        } else if (exch.toUpperCase() == "XSHE") {
            return "137";
        } else if (exch.toUpperCase() == "XLON" || exch.toUpperCase() == "LSE") {
            return "151";
        } else {
            return "126";
        }
    }
    var connectObj;
    function connectToPusServer() {
    	if(chartObj.isConnectedToPushServer) return;
    	//jslib.connect(operationHandler, "connectClosure", "http://push.morningstar.com/", updateHandler);
    	connectObj = jslib.connectEntitlement(operationHandler, "connectClosure", pushServer, "mstar|Terminal","1","2",updateHandler);
    	connectObj.addFields(operationHandler, "addFieldsClosure", "LastPrice","Time","Volume"); 
    	chartObj.isConnectedToPushServer = true;
      }
    function bacthUnscribeTickers(pids){
		if(typeof(chartObj.tValidation)== "object" && typeof(gIdMap)== "object"){ // QSII terminal
			chartObj.tValidation.unSubscribe(pids);
		}else{
			unsubscribeStock(pids);
		}
	}
    function subscribeStock(pids) { 
		if(!pids || !pids.length || !connectObj){
			return;
		}
		var info, code, item, type;
        connectObj.subscribe(operationHandler, "subscribeClosure", "D", code, item);
		var str = "connectObj.subscribe(operationHandler, \"subscribeClosure\"";
        for (var j = 0; j < pids.length; j++) {
            info = tickerInfos[pids[j]];
			if(!info) continue;
			type = "D";
			code = info.country == "USA"?"126":info.tenforeCode;
		    item = info.type+"_"+info.tenforeTicker+"_-1";
            str += ", \"" + type + "\", \"" + code + "\", \"" + item + "\"";  // jacye
            if ((j + 1) % 50 == 0 || j == pids.length - 1) {
                str += ")";
                eval(str);
                str = "connectObj.subscribe(operationHandler, \"subscribeClosure\"";
            }
        }
    }
    
    function unsubscribeStock(pids) {
       if(!pids || !pids.length || !connectObj){
			return;
		}
		var info, code, item, type;
        connectObj.subscribe(operationHandler, "subscribeClosure", "D", code, item);
		var str = "connectObj.unsubscribe(operationHandler, \"subscribeClosure\"";
        for (var j = 0; j < pids.length; j++) {
            info = tickerInfos[pids[j]];
			if(!info) continue;
			type = "D";
			code = info.country == "USA"?"126":info.tenforeCode;
		    item = info.type+"_"+info.tenforeTicker+"_-1";
            str += ", \"" + type + "\", \"" + code + "\", \"" + item + "\"";  // jacye
            if ((j + 1) % 50 == 0 || j == pids.length - 1) {
                str += ")";
                eval(str);
                str = "connectObj.unsubscribe(operationHandler, \"subscribeClosure\"";
            }
        }
    }
     
    function operationHandler(closure, eventType, event, body) {
    	if (eventType == jslib.OPERATION_ERROR) {
    		var err;
    		switch (event) {
    			case jslib.ERROR_CONNECTED:
    				err = "You are already connected";
    				break;
    			case jslib.ERROR_NOT_CONNECTED:
    				err = "You are not connected";
    				break;
    			case jslib.ERROR_TIMEOUT:
    				err = "Connection timeout";
    				break;
    			case jslib.ERROR_HTTP:
    				err = "HTTP status error. The HTTP status is: " + body;
    				break;
    			case jslib.ERROR_SERVER:
    				err = "Server side error: " + body;
    				break;
    			case jslib.ERROR_DOMAIN:
    				err = "Domain error.";
    				break;
    			case jslib.ERROR_SERVER_DOWN:
    				err = "Server down.";
    				break;
    		}
    		writeTimeLog(err);
    	} else if (eventType == jslib.OPERATION_OK) {
    		// everything is ok
    	 }
       }
       
    function updateHandler(closure, eventType, event, body) {
    	if (eventType == jslib.STREAMING_ERROR) {
    		alert("Streaming error=" + event + ", body=" + body);
    	} else if (eventType == jslib.MESSAGE_RECEIVED) {
    		switch (event) {
    		case jslib.MARKET_PRICE_UPDATE:
    			updateMarketPrice(body);
    			break;
    		case jslib.STATUS_UPDATE:
    			updateStatus(body);
    			break;
    		}
    	}
    }
    
    this.updateData = function(gKey,aData) {
         var arr = gKey.split(":");
         if (gData == null || gData[gKey] == null) {
             return;
         }
         var data = gData[gKey];
		 var pid = gData[gKey]["performanceId"];
         if(tickerInfos[pid]) {
             if (aData["LastPrice"] != null) {
                 cacheIntradeData(aData["LastPrice"]["value"],data["Time"],gKey,"p");
             }
             if (aData["Volume"] != null ) {
                 cacheIntradeData(aData["Volume"]["value"],data["Time"],gKey,"v");
             }
         }
         
    } 
 
    function updateMarketPrice(body) {
        var result = {};
        var itemsReceived = body.items;
        var fieldsForItem;
        for (var i = 0; i < itemsReceived.length; i++) {
		    result.sourceType = itemsReceived[i].source;
            result.serviceName = itemsReceived[i].service;
            result.itemName = itemsReceived[i].symbol;
            result.dataType = result.sourceType;  // D or R.
            if (result.serviceName == "17" || result.serviceName =="14" || result.serviceName == "19" || result.serviceName == "1") {                      // bats data 17.
                result.serviceName = "126";
            }
            aL = result.itemName.split("_");
            result.itemName = aL[1];
            result.subcode = aL[2];
            fieldsForItem = itemsReceived[i].fields;
		   // We received the item "itemName" along the service "serviceName", from the source type "sourceType
		    var price=null, vol = null, time=0, fieldName, value;
		    for (var j = 0; j < fieldsForItem.length; j++) {
			    fieldName = fieldsForItem[j].name;
			    value = fieldsForItem[j].value;
			    if(fieldName=="LastPrice") price=parseFloat(value);
			    if(fieldName=="Time") time = value;//startTick+parseInt(value)*60000; 				    
		    }
		    if(price != null && time != 0) {
		        cacheIntradeData(price,time,result.serviceName+":"+result.itemName,"p");
		    }
		    if(vol != null && time != 0) {
		        cacheIntradeData(vol,time,result.serviceName+":"+result.itemName,"v");
		    }
		}
    }
    
    function updateStatus(body) {
	        var sourceType = body.source;
	        var serviceName = body.service;
	        var itemName = body.symbol;
	        var itemState = body.state;
	        var itemStatus = body.status;
	        msg += "Status update, source type='" + sourceType + "', item='" + serviceName + ":" + itemName + "'\nstate=" + itemState + "\nstatus=" + itemStatus + "\n\n";	
	        alert(msg);
    }
    //autoComplete
	this.autoCPCallback=function(o){
        this.acceptTicker(o);
    };
};
     
